ubuntuusers.de

Mit Java Systemvariablen setzen

Status: Gelöst | Ubuntu-Version: Ubuntu
Antworten |

marcelh

(Themenstarter)

Anmeldungsdatum:
31. März 2007

Beiträge: 30

so, das Problem ist gelöst! Vielen vielen Dank.

Lösung:

String[] envp = {"DISPLAY=:0", "XAUTHORITY=/home/marcel/.Xauthority"};
Runtime.getRuntime().exec("audacious -p", envp);

Sid_Burn

Anmeldungsdatum:
23. Oktober 2004

Beiträge: 2159

user unknown hat geschrieben:

Sid Burn hat geschrieben:

Du änderst zwar die Variablen, allerdiengs sind diese dann nur gültig für dein Programm, beziehungsweise für alle Childprozesse die du erzeugst.

Nicht beziehungsweise, sondern nur für diesen einen, neugestarteten Prozeß - nicht für andere, und nicht für den, in dem das Programm schon läuft (ohne es getestet zu haben).

Häh?
Irgendwie verstehe ich deine Antwort nicht so ganz.
Wenn ich die Umgebungsvariable neu setzen in einem programm dann sollten die auch sofort für mein Programm gelten. Und da jeder fork() Automatisch eine Kopie des Parents ist, hat auch jeder Child Prozess die gleichen veränderten Umgebungsvariablen wie der Parent.

#!/usr/bin/perl
use warnings;
use strict;

# Aktuelle pid und ppid
printf "PID: %s PPID: %s\n", $$, getppid();
# PATH Variable Ausgeben
print "PATH: $ENV{PATH}\n";
# PATH Variable neu setzen.
$ENV{PATH} = '/bin';
# PATH wieder ausgeben
print "PATH: $ENV{PATH}\n\n";

# Forken
my $pid = fork;
die "Fehler bei fork: $!\n" if not defined $pid;

if ($pid) {
    #Parent
    printf "Parent: PID: %s PPID %s\n", $$, getppid();
    print "Parent: PATH: $ENV{PATH}\n";
}
else {
    #Child
    printf "Child: PID: %s PPID %s\n", $$, getppid();
    print "Child: PATH: $ENV{PATH}\n";
}

Das Programm gibt bei mir folgendes aus:

PID: 6768 PPID: 5395
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games
PATH: /bin

Child: PID: 6769 PPID 6768
Child: PATH: /bin
Parent: PID: 6768 PPID 5395
Parent: PATH: /bin

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Sid Burn hat geschrieben:

user unknown hat geschrieben:

... nur für diesen einen, neugestarteten Prozeß - nicht für andere, und nicht für den, in dem das Programm schon läuft (ohne es getestet zu haben).

Häh?
Irgendwie verstehe ich deine Antwort nicht so ganz.
Wenn ich die Umgebungsvariable neu setzen in einem programm dann sollten die auch sofort für mein Programm gelten. Und da jeder fork() Automatisch eine Kopie des Parents ist, hat auch jeder Child Prozess die gleichen veränderten Umgebungsvariablen wie der Parent.

Das sagst Du so.
In Perl mag das so sein - in Java ist es nicht so.
Die JVM wird mit einem Environment gestartet, und mein Programm startet einen neuen Prozeß, der die Shell aufruft, und dort das Environment ändert.
Das fände ich schon äußerst überraschend, wenn diese Änderung sich auf den Parent auswirken würde.

Wenn ich das richtig sehe, dann bleibt bei Deinem Perlprogramm das Programm im Perlkontext, und ruft nie ein Systemprogramm auf - daher wundert es nicht, daß Änderungen an der Umgebung im Kontext auch bekannt sind.

import java.util.*;
import java.io.*;

public class SysVar
{
	public SysVar () 
	{
		sysProps ();
		System.setProperty ("FOO", "42");
		System.setProperty ("LANG", "en_US.UTF-8");
		String [] command = {"/bin/bash", "-c", "export PATH=$PATH:/no/such/PFAT; echo $PATH; echo $LANG; echo $FOO"};
		try 
		{
			Runtime runtime = Runtime.getRuntime ();
			Process proc = runtime.exec (command);
			BufferedReader br = new BufferedReader (new InputStreamReader (proc.getInputStream ()));
			String line = null;
			System.out.println ("===========================");
			do
			{
				line = br.readLine ();
				if (line != null) 
					System.out.println (line);
			}while (line != null);
		}
		catch (IOException ex) 
		{
			ex.printStackTrace ();
		}
		sysProps ();
	}
		
	public void sysProps ()
	{
		System.out.println ("---------------------------");
		Map <String, String> m = System.getenv ();
		System.out.println (m.get ("PATH"));
		System.out.println (m.get ("LANG"));
		System.out.println (m.get ("FOO"));
	}

	public static void main (String args[])
	{
		new SysVar ();
	}
}

Die Ausgabe hier ist:

\––\––\––\––\––\–––-
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/opt/java/bin:/opt/ant/bin:/usr/local/pgsql/bin/:/home/stefan/bin
de_DE.UTF-8
foobar

======================
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/opt/java/bin:/opt/ant/bin:/usr/local/pgsql/bin/:/home/stefan/bin:/no/such/PFAT
de_DE.UTF-8
foobar
\––\––\––\––\––\–––-
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/opt/java/bin:/opt/ant/bin:/usr/local/pgsql/bin/:/home/stefan/bin
de_DE.UTF-8
foobar

Interessanterweise ändert selbst der Aufruf von System.setProperty (key, value) offenbar nichts - das verwundert auch mich.
Eine nicht allzu ausgiebige Suche hat mir dazu noch nicht die Augen geöffnet.
Ob ich gänzlich neue Werte setze wie FOO, oder bestehende ändere wie LANG - weder ändert sich was, noch wird eine Exception geworfen.

Die einzige Änderung ist der PATH in dem exec () - Aufruf, wo wie gewollt '/no/such/PFAT' auftaucht.

Lunar

Anmeldungsdatum:
17. März 2006

Beiträge: 5792

radoe2 hat geschrieben:

Naja, extra eine Shell starten um das Prozessenvironment des aufzurufenden Programms zu ändern ist nun auch nicht wirklich schön.

Wieso? Das ist unter Linux ziemlich üblich... Das berühmte system() aus C macht auch nichts anderes als eine Shell zu starten. Unter anderem deswegen ist es unter Linux ja so wichtig, dass unter /bin/sh eine POSIX kompatible Shell liegt.

radoe2 hat geschrieben:

Erstens fällt mir da spontan dieser Vergleich ein, in dem Kanonen und Spatzen vorkommen, zweitens handelt man sich ggf. (nicht unbedingt in diesem Beispiel) unschöne Sicherheitslücken ein.

Wie gesagt, dass Starten von Programmen wird unter Linux meistens über eine Shell durchgeführt. Andernfalls ist dass nämlich nur mittels einer Kombination aus fork() und exec*() zu erreichen, was den Code zum Starten eines einfachen Programms schnell in den zweistelligen Zeilenbereich bringt. Dieser sichere Weg hat viel eher mit Kanonen und Spatzen zu tun 😉

user unknown hat geschrieben:

Wenn ich das richtig sehe, dann bleibt bei Deinem Perlprogramm das Programm im Perlkontext, und ruft nie ein Systemprogramm auf - daher wundert es nicht, daß Änderungen an der Umgebung im Kontext auch bekannt sind.

Was soll den der "Perlkontext" sein? Durch das Forking wird ein neuer Prozess gestartet. Um es deutlich zu machen: Nach dem Fork existieren auf Sids System zwei Perl-Prozesse, von denen der eine den if und der andere den else Zweig abhandelt. Da gibt es keinen gemeinsamen "Perlkontext" mehr.

Das funktioniert so in jeder Sprache: Man kann Sids Beispiel auch in Python oder C implementieren. Man kann die Sprachen sogar mischen: Statt die PID und den PATH im Else Zweig mit Perl auszugeben, hätte Sid auch ein kleines C Programm dazu schreiben können, welches er mittels execvp aufruft. Das Ergebnis wäre exakt das gleiche gewesen.

Das beobachtete Verhalten hat nichts mit Perl zu tun, sondern ist das normale Verhalten des Systemaufrufs fork unter Linux.

Sid_Burn

Anmeldungsdatum:
23. Oktober 2004

Beiträge: 2159

@user unknown

Die JVM wird mit einem Environment gestartet, und mein Programm startet einen neuen Prozeß, der die Shell aufruft, und dort das Environment ändert. Das fände ich schon äußerst überraschend, wenn diese Änderung sich auf den Parent auswirken würde.

In meinen Beispiel hat kein Child irgendwas am Parent verändert. Das ist ja nicht Möglich.

Es ist so wie Luna sagte. Nach einem fork() hat man zwei voneinander getrennte Prozesse die sozusagen an der selben Codezeile weiter arbeiten. Der Parent Prozess erfüllt die Bediengung für das if (dieser enthält nämlich die PID des Child Prozesses, und beim Child Prozess selber enthält $pid eine 0). Erfüllt also nicht die if Bediengung und springt im else Zweig.

Ansonsten gibt es keinen gemeinsamen "Kontext". Ich kann auch beliebig mit system() irgendetwas aufrufen oder mit fork() und exec() arbeiten.

env.pl

#!/usr/bin/perl
use warnings;
use strict;

print "$ENV{PATH}\n";

$ENV{PATH} = '/bin';
system('echo $PATH');
system('./path.pl');

my $pid = fork;
die "Cannot fork: $!\n" if not defined $pid;

if ($pid) {
    # Parent
    exit 0;
}
else {
    exec './path.pl';
}

path.pl

#!/usr/bin/perl
use warnings;
use strict;

print "$0: $ENV{PATH}\n";

Einmal rufe ich dann path.pl über system() und einmal über fork() und exec() auf. Wenn ich die Umgebungsvariablen ändere und dann forke dann werden diese änderungen wie bereits gesagt auf alle Childs übertragen. Den das Perl Programm ist der Parent und gibt die Systemvariablen vor, und es wird nicht irgendwie eine komplett unabhängige Shell gestartet die dann erst mein Programm aufruft.

Wenn ich env.pl starte kommt dann letztendlich folgendes heraus:

sidburn@sid ~/perl/env $ ./env.pl
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games
/bin
./path.pl: /bin
./path.pl: /bin

Wenn ich nur path.pl starte logischerweise das hier.

sidburn@sid ~/perl/env $ ./path.pl
./path.pl: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games

Wenn sich das ändern der Umgebungsvariable nicht auf den aktuellen Prozess auswirkt, und auch nicht auf alle Child prozesse, welchen Sinn würde es denn dann überhaupt geben Funktionen anzubieten die Umgebungsvariablen verändern können, letztendlich aber doch keine Auswirkung haben?

Ansonsten glaube ich das wir uns beide entweder nicht verstehen, etwas anderes meinen, oder aneinander vorbeireden. oO

radoe2

Anmeldungsdatum:
30. November 2006

Beiträge: 243

Lunar hat geschrieben:

radoe2 hat geschrieben:

Naja, extra eine Shell starten um das Prozessenvironment des aufzurufenden Programms zu ändern ist nun auch nicht wirklich schön.

Wieso? Das ist unter Linux ziemlich üblich... Das berühmte system() aus C macht auch nichts anderes als eine Shell zu starten. Unter anderem deswegen ist es unter Linux ja so wichtig, dass unter /bin/sh eine POSIX kompatible Shell liegt.

radoe2 hat geschrieben:

Erstens fällt mir da spontan dieser Vergleich ein, in dem Kanonen und Spatzen vorkommen, zweitens handelt man sich ggf. (nicht unbedingt in diesem Beispiel) unschöne Sicherheitslücken ein.

Wie gesagt, dass Starten von Programmen wird unter Linux meistens über eine Shell durchgeführt. Andernfalls ist dass nämlich nur mittels einer Kombination aus fork() und exec*() zu erreichen, was den Code zum Starten eines einfachen Programms schnell in den zweistelligen Zeilenbereich bringt. Dieser sichere Weg hat viel eher mit Kanonen und Spatzen zu tun 😉

Die Shell ist unnötig in diesem Fall und bringt erstens Overhead und hat zweitens in den meisten Fällen Sicherheitsimplikationen, die es zu bedenken gilt. Als jemand, der Perl und system() als Beispiele anführt, solltest du das wissen. Ausserdem ging es hier weder im Linux im allgemeinen und fork()/exec() im besonderen, sondern um Java und System.exec() (und später dann um den eleganteren Weg mit dem ProzessBuilder). Diese nehmen dir den ganzen Aufwand (ja, ich habe sehr häufig das Vergnügen, Nebenläufigkeit in C,Perl und Python gemischt realisieren zu dürfen) der fork()/dup()/close()/exec()/wait() Kaskaden ab. Aber darum ging es hier doch gar nicht.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

@radeo: Eben. Genau.
@Sid Burn: Du setzt das Environment um, und rufst dann system auf - es bekommt also das geänderte Environment zu Gesicht.
Das überrascht mich nicht, und habe ich nie bezweifelt.

Der Java-exec-Aufruf startet einen neuen Prozeß mit dem alten Environment, und ändert dies, und das das aufrufende Javaprogramm diese Änderung nicht von seinem Child erbt - das überrascht Dich doch auch nicht?

Das Javaprogramm, um diese Behauptung zu bewisen habe ich ja nun gepostet.

@Lunar: Dito.

Was hat es übrigens mit den Sicherheitsbedenken auf sich, die ihr äußert?
Ist das nicht ein vager Transfer von Webservertugenden in die Applikationswelt?
Den meisten Unheil, den die Systemcommandos verursachen könnten, könnte man auch gleich in Java anrichten - ich sehe da eigentlich keine Sicherheitslücke.

Sid_Burn

Anmeldungsdatum:
23. Oktober 2004

Beiträge: 2159

@Sid Burn: Du setzt das Environment um, und rufst dann system auf - es bekommt also das geänderte Environment zu Gesicht.
Das überrascht mich nicht, und habe ich nie bezweifelt.

Ok, dann sind wir da einer Meinung.
Obwohl ich aus deinem ersten Post wo du mich gequotet hast irgendwie etwas anderes heraus gelesen habe.

Der Java-exec-Aufruf startet einen neuen Prozeß mit dem alten Environment, und ändert dies, und das das aufrufende Javaprogramm diese Änderung nicht von seinem Child erbt - das überrascht Dich doch auch nicht?

Natürlich überascht es mich nicht das ein Parent nicht die Änderung von sein Child erbt. Etwas anderes habe ich auch nie behauptet.

Wenn ich also zusammenfassen darf.

Man kann Systemvariablen für sein eigenen Prozess ändern, diese sind auch nur für diesen prozess gültig, und alle Childs "vererben" diese veränderten Systemvariablen. Damit sind wir nun wieder genau an der Behauptung angekommen die ich in meinen aller ersten Post schrieb:

Du änderst zwar die Variablen, allerdiengs sind diese dann nur gültig für dein Programm, beziehungsweise für alle Childprozesse die du erzeugst.

Und an dieser Behauptung hattest du ja irgendetwas auszusetzen?!?

Als jemand, der Perl und system() als Beispiele anführt, solltest du das wissen.

Wie du bereits richtig sagst sind dies Beispiele.
Beispiele dienen nicht dazu Ultra Sicher zu sein, sondern nur um etwas zu "Zeigen".
Ich Persönlich würde in keiner ernsthaften Applikation system() Nutzen.
Alleine schon deswegen weil es die Portabilität senkt.
Ansonsten senkt es natürlich auch die Performance, da ein fork() ausgeführt wird.
Das hat man bei normalen Perl Programmen oder Modulen etc. alles einfach nicht.

Reicht eigentlich schon als begründung system() nicht zu nutzen. 😉

Was hat es übrigens mit den Sicherheitsbedenken auf sich, die ihr äußert?
Ist das nicht ein vager Transfer von Webservertugenden in die Applikationswelt?
Den meisten Unheil, den die Systemcommandos verursachen könnten, könnte man auch gleich in Java anrichten - ich sehe da eigentlich keine Sicherheitslücke.

system() kann zur ersten Sicherheitslücke werden wenn du dort einfach Code ausführst den z.B. Benutzer eingegeben haben. Wenn dein Programm mit root Rechten läuft, z.B. Webserver und ein entsprechendes Skript und ein Benutzer dort einen String eingibt und er durch gute Wahl des Strings ein "rm -rf /" ausführen kann, hat er somit dein Rechner gelöscht.

Klar kannst du auch ein Java Programm schreiben das mit "root" rechten das gleiche macht. Aber es ist ja wohl ein Unterschied ob du selber das Programm startest und schreibst und bereits mit vorhandenen root rechten ausführst.

Oder ob 8 Milliarden Menschen auf der Welt mal eben einen String eingeben können, und dann etwas passiert was du nicht wolltest. 😉

Andere Problematik ist einfach die, dass du dir nie Sicher sein kannst was da nun wirklich ausgeführt wird. Führt ein "ls -l" nun wirklich das "ls" aus was du meinst oder ein fremdes untergeschobenes "ls"?

Ansonsten zur Portabilität. Vielleicht hast du ein OS was diese Shellkommandos gar nicht kennt. Damit läuft dann dein programm nicht mehr. Gerade bei den Unterschiedlichen Linux Versionen kann es passieren das mal eine andere Version eines Tools installiert ist. Damit kennt es z.B. manche Kommandozeilen Optionen nicht. Auch zwischen BSD und Linux passiert es oft das Kommandoswitches halt eine andere Funktionalität haben.

Aber die letzten Sache gelten Generell für jede Art von Shell Skript.

Ich meinte ich kannte mal mehr gravierende Sicherheitslücken die man schnell mit system() einbauen kann. Aber kenne diese nicht mehr. Hab mir nur im Kopf behalten system() einfach nicht nutzen und fertig.

Aber wäre gut wenn radoe2 nochmal paar Wörter dazu sagt.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Ja.
Im Zusammenhang mit marcelh's Codeschnipsel bezog ich das 'Programm' auf sein Javaprogramm.
Und sein Programm erzeugt ja nur einen neuen Prozess, der Systemvariablen temporär für sich (und seine - wenn es denn welche hätte - Kindprogramme) ändert, aber nicht für sein (Java-)Programm.

Ein hübsches Aneinandervorbeireden also.

Lunar

Anmeldungsdatum:
17. März 2006

Beiträge: 5792

radoe2 hat geschrieben:

Die Shell ist unnötig in diesem Fall und bringt erstens Overhead und hat zweitens in den meisten Fällen Sicherheitsimplikationen, die es zu bedenken gilt. Als jemand, der Perl und system() als Beispiele anführt, solltest du das wissen.

Ich und Perl? Bewahre mich vor diesem Unheil 😉 Sid ist hier der Perl-Jünger, meine Wenigkeit liebt Python. Ich glaube, du hast da ein paar Leute hier zusammen geworfen 😉

Sicher ist Overhead dabei, aber wenn ich schnell ein kleines Programm nur für den privaten Gebrauch in C schreiben wollte, würde ich system() verwenden, schon allein deswegen, weil es schneller zu coden ist. In diesem Szenario sind die Sicherheitsprobleme eher zweitrangig, weil der potentielle Nutzer auch gleich dem Programmierer ist. Ich werde wohl kaum meinen eigenen Rechner angreifen 😉 Das man system() in ernsthaften Anwendungen nicht benutzt, darüber brauchen wir nicht diskutieren.
Und der Overhead? Auf einem modernen Desktop PC mit 1 GB Ram und >2 GHz Prozessorpower? Diesen Nachteil sehe ich nicht wirklich... 😉 Da könnte man ja auch sagen, meine Python-Programme wären Overhead im Vergleich zur Umsetzung in C...

radoe2 hat geschrieben:

Ausserdem ging es hier weder im Linux im allgemeinen und fork()/exec() im besonderen, sondern um Java und System.exec() (und später dann um den eleganteren Weg mit dem ProzessBuilder). Diese nehmen dir den ganzen Aufwand (ja, ich habe sehr häufig das Vergnügen, Nebenläufigkeit in C,Perl und Python gemischt realisieren zu dürfen) der fork()/dup()/close()/exec()/wait() Kaskaden ab. Aber darum ging es hier doch gar nicht.

Nun, aber es ging darum, ob der Weg über die Shell zum Starten eines neuen Prozesses nun umständlich oder gar übertrieben wäre. Das sehe ich nicht so. Zum einen ist es ziemlich üblich, Prozesse über eine neue Shell zu starten (trotz der einhergehenden Risiken). Aus deiner Position gesehen, wäre der gesamte Boot-Prozess eines Linux-Systems ein einziger Overhead, weil er ausschließlich über Shell-Skripte läuft.

Antworten |