Hallo ich brauche Hilfe für einen freund / Hello I need help for a friend
hier die Infos /Angabe / here the details / information
Thema: Systemzugriffe zu Prozesssteuerung und zur File-&Dir-IO
Wir verwenden in den Übungen je einen Solaris und einen Linux Rechner.
Erzeugen Sie diese 2 virtuellen Maschinen, indem Sie die entsprechenden Clones von den Images mit den darauf schon installierten Compilern bilden. Nennen Sie Ihre Rechner VNLi und VNSol (VN sind Ihre Iniitialen).
Erzeugen Sie auf einer Maschine das Verzeichnis /usr/progs in dem Sie die Programme platzieren (am besten jedes Beispiel in einem Subdirectory). Mounten Sie dieses Verzeichnis permanent auf der anderen Maschine im gleichen Pfad, damit die Programme einfach auf beiden Systemen getestet werden können.
Abzugeben sind jeweils am Tag vor den Übungseinheiten die gut dokumentierten Listings als PDF sowie ein tar-Archiv mit Source-Code und Makefile zum Übersetzen auf beiden Zielplatformen.
Übersetzen Sie die Programme sowohl auf Linux als auch Solaris und testen Sie die Funktion auf beiden Systemen!
Erweitern Sie die Shell aus Übung 1 um folgende Kommandos, die als Shell-interne Kommandos realisiert werden sollen:
1. infos Gibt den Usernamen zur effektiven UID, das Working Directory und die File-Creation Mask aus
2. cd Wechselt das Verzeichnis
3. listdir Akzeptiert 0 oder 1 Argument und gibt den Verzeichnisinhalt (bei 0 Argumenten das aktuelle VZ) aus.
Geben Sie folgende Spalten in der angeführten Reihenfolge aus (mit ordentlicher Überschrift, sodass sich auch nicht-Ux User auskennen):
Dateiname, Besitzername, Besitzergruppe, Typ, XXXXX, Rechte
Für XXXXX setzen Sie folgendes ein, entsprechend der letzten Ziffer ihrer Matrikelnummer:
Letzte Ziffer in Matrikelnummer Auszugebende Info
0,5 Inode-Nummer
1,6 Link-Counter
2,7 Letzte Zugriffzeit (atime)
3,8 Letzte Änderungszeit (mtime)
4,9 Letzte Statusänderung (ctime)
Geben Sie standardmäßig in der Reihenfolge aus, in der die Einträge gelesen werden.
Dabei soll der Typ als Wort ausgeschrieben werden (also zB: „Datei“, „Verweis“, „Verzeichnis“, „Geraet“, „FIFO“) und in der Spalte Rechte nur die Rechte angeführt werden, die der Aufrufer des Programms `listdir´ an der Datei hat (wir gehen nur von rwx aus! Kein ACLs, wie etwa in zfs). Wenn ein anderer Benutzer das Programm aufruft, kann also bei „was darf ich“ bei den selben Dateien was anderes stehen.
Die Ausgabe könnte also etwa so aussehen:
Dateiname Besitzer/Gruppe Typ Groesse Was darf ich
DateiA karl/tm05 Datei 34.556 Lesen/Schreiben
Dirxy sepp/tm23 Verzeichnis 423 Lesen/Ausführen
MeinStick karl/Gruppexy Geraet - Lesen/Schreiben
X-File susi/455 Datei 4.456 nichts
……
4. Programmieren Sie abhängig von Ihrer Matrikelnummer ebenfalls als Shell-Interne Befehle:
Letzte Ziffer in Matrikelnummer Zu programmierender Befehl
0,6 cpy
1,7 mov
2,8 hln
3,9 sln
4,5 rename
Verwenden Sie generell Systemcalls und NICHT die C-Streams (stdio)!
cpy: Funktioniert etwa wie cp ohne Optionen:
Syntax: cpy datei 1 zieldatei
cpy datei1 *datei 2 …. + zieldir
Vergessen Sie Pfade nicht, so muss auch das korrekt funktionieren:
cpy /export/home/karl/a* ../hugo
Achten Sie bei cpy darauf, dass die Rechte am neuen Objekt möglichst genau den Rechten an der Originaldatei entsprechen, selbst dann, wenn der Aufrufer eine umask hat!
mov: Funktioniert etwa wie mv ohne Optionen:
Syntax: mov datei 1 zieldatei
mov datei1 *datei 2 …. + zieldir
Vergessen Sie Pfade nicht, so muss auch das korrekt funktionieren:
mov /export/home/karl/a* ../hugo
Achten Sie bei mov darauf, dass die Rechte am neuen Objekt möglichst genau den Rechten an der Originaldatei entsprechen, selbst dann, wenn der Aufrufer eine umask hat!
Achten Sie bei mov darauf, dass der Aufwand innerhalb eines Filesystems klein bleibt, aber auch zwischen Filesystemen verschoben werden darf!
hln: Erzeugt Hardlinks und funktioniert etwa wie ln ohne Optionen:
Syntax: hln datei 1 zieldatei
hln datei1 *datei 2 …. + zieldir
Vergessen Sie Pfade nicht, so muss auch das korrekt funktionieren:
cpy /export/home/karl/a* ../hugo
Achten Sie bei hln darauf, dass diese nur innerhalb eines Filesystems und nicht auf Directories angewandt werden können. Geben Sie ausführliche verständliche Fehlermeldungen aus, wenn das versucht würde.!
sln: Erzeugt symbolic Links und funktioniert etwa wie ln -s ohne weitere Optionen:
Syntax: sln datei 1 zieldatei
sln datei1 *datei 2 …. ] zieldir
Vergessen Sie Pfade nicht, so muss auch das korrekt funktionieren:
cpy /export/home/karl/a* ../hugo
Wenn ein Hardlink auch möglich wäre, soll eine Rückfrage kommen, ob tatsächlich ein symbolic Link gewünscht wird und dieser nur erzeugt werden, wenn die Frage mit j/y beantwortet wird.
rename: Benennt Files um
Syntax: mov alt neu Datei1 * Datei2, ….+
Vergessen Sie Pfade nicht, so muss auch das korrekt funktionieren:
mov abc xxyzz /export/home/karl/a*
In allen Dateinamen (nicht in den Pfaden!) der Argumente, die ab dem 3. Parameter angegeben wurden, werden alle Vorkommen des Strings „alt“ (1. Argument) durch „neu“ (2. Argument) ersetzt. Das Kommando akzeptiert nur „reguläre Dateien“. Für Argumente, die Verzeichnisse oder Geräte sind, wird eine Fehlermeldung ausgegeben.
5. Implementieren Sie den Kommandosuchpfad folgendermaßen:
Beim Start wird die Environmentvariable PATH ausgelesen und als Anfangswert übernommen.
Ein Shell interner Befehl namens ‚setpath‘ erlaubt das Festlegen eines anderen Kommandosuchpfades, in dem beliebig viele Pfade Zeile für Zeile eingegeben werden (bis z B eine Leerzeile- nur Return gedrückt- eingegeben wird). Für jedes eingegebene Verzeichnis wird dabei geprüft, ob es ein gültiges Verzeichnis ist.
Executables werden von unserer Shell danach nur mehr an diesen Orten gesucht!
wie muss das aussehen in C++ das war noch mit da bei
//Meine erste Shell
#define MAXLINE 3000
#define MAXWORDS 500
using namespace std;
bool zeile_zerlegen(char **cmdv, int &numw, char *zeile);
void ausfuehren(char **cmdv, int numwords);
int main(int argc, char **argv, char **envp)
{
char zeile[MAXLINE]; //wir schreiben hier ein C Programm, das nur die C++ IO verwendet
char *cmdv[MAXWORDS];
int numw;
bool hg;
void ausfuehren(char **cmdv, int numwords)
{
int pid, ret;
if (!strcmp(cmdv[0],"ende"))
exit(numwords>1 ? atoi(cmdv[1]) : 0);
switch(pid=fork())
{
case 0: //bin der Sohn der die Hackn macht
execvp(cmdv[0], cmdv);
perror ("cannot start programm: ");
exit(1);
break;
case -1:
perror ("cannot fork: ");
break;
default:
cout << "Ich bin die Vatershell und hab einen Sohn mit Arbeit versorgt" << endl;
waitpid(pid, &ret, 0);
break;
}
}
was er bis jetzt gemacht hat und die 2 angabe dazukommen gleich
Thema: System-Calls zum Process-Management
Erweitern Sie die Shell folgendermaßen, entsprechend der letzten Ziffer ihrer Matrikelnummer:
Letzte Ziffer in Matrikelnummer zu implementierende Funktion
0,7 a) Ein-/Ausgabeumleitung
1,8 b) Signalbehandlung
2,9 c) Job-Control
3,5 d) Prompt-Definition
4, 6 e) Restricted Version + Loginshell
a) Einfache Ein- / Ausgabe Umleitung auf Dateien (muss nur für externe Commands funktionieren, bei internen Commands wird sie ignoriert): < Datei stdin von Datei lesen(open-read) > Datei stdout UND stderr auf Datei schreiben(open-creat) >> Datei stdout UND stderr an Datei anhängen(open-append)
Die Umleitung kann dabei wie in wirklichen Shells an beliebiger Stelle der Kommandozeile stehen. Das Umleitungszeichen sowie der Dateinamen müssen jeweils als eigenes Wort auftreten.
b) Signalbehandlung:
a. Implementieren Sie den Befehl „trap“:
trap Signalnummer Kommando mit Argumenten
Sehen Sie dafür ein Array von Kommandos (zB einfach den String) vor, das mit der Signalnummer indiziert wird. Will einer das Signal 9 fangen, gibt’s natürlich eine Fehlermeldung.
b. Implementieren Sie den Befehl untrap, der den Eintag aus dem Array wieder entfernt:
trap Signalnummer Kommando mit Argumenten
c. Fangen Sie die Signale, für die es einen trap-Eintrag gibt und führen Sie bei Autreten des Signals das hinterlegte Kommando aus.
c) Job-Control Machanismus:
Jeder Hintergrund-Prozess wird unmittelbar nach seiner Erzeugung vom Shell-Thread (main-thread) in eine Liste laufender Prozesse geschrieben. (Kommandoname, PID und aktuelle Uhrzeit)
Bei Hintergrundjobs kriegt ja ein Signalhandler, der auf SIGCHILD gesetzt ist, das Terminieren eines Hintergrundjobs mit, und bei Vordergrund-Jobs wartet ja eh die Shell. Implementieren Sie den Signalhandler, der beim Terminieren eines der Hintergrundprozesse die Liste korrigiert – also den terminierten Prozess aus der Liste gelöscht.
Ein Shell internes Kommando „show-bg“ ist zu implementieren, das alle noch laufenden Backgroud-Jobs mit Name, PID und Startzeit anzeigt – also die Liste ausgibt.
Achten Sie darauf, dass beim Zugriff auf die Liste, der ja vom Signalhandler und dem Kommando show-bg gleichzeitig erfolgen kann, keine Race-Conditions auftreten können!
d) Prompt Definition
Implementieren Sie einen Prompt, der die Varialbe PS1 verwendet.
Dabei sollen folgende backslash-escaped special characters wie in der bash implementiert werden:
\d the date in "Weekday Month Date" format (e.g., "Tue May 26")
\h the hostname up to the first `.'
\H the hostname
\l the basename of the shell's terminal device name
\n newline
\t the current time in 24-hour HH:MM:SS format
\u the username of the current user
\w the current working directory, with $HOME abbreviated
with a tilde
\W the basename of the current working directory, with $HOME
abbreviated with a tilde
\# the command number of this command
\\ a backslash
Variablensubstitutionen im Prompt, wie sie die bash oder ksh kann, sind nicht erforderlich.
e) Restricted Version + Loginshell:
a. Wenn die Shell mit einem Namen aufgerufen wird, der mit – beginnt, dann soll die Shell sich als Login-Shell benehmen und das Script $HOME/.myprofile abarbeiten. Natürlich darf das Script nur Zeilen enthalten, die unsere Shell verarbeiten kann (also zB keine Wildcards, Variablensubstitutionen, Schleifen, etc…)
b. Wenn die Shell mit einem Namen aufgerufen wird, der mit r… oder –r… beginnt, dann soll unsere Shell als restricted Shell mit folgenden Einschränkungen arbeiten:
- Der Kommandosuchpfad kann nicht interaktiv geändert werden (nur über das .myprofile)
- Der cd Befehl ist nicht erlaubt
- Programmaufrufe mit Pfadangabe im Kommando sind nicht erlaubt (es können also nur Kommandos von den Orten gestartet werden, die im Kommandosuchpfad angegeben sind.
bool zeile_zerlegen(char **cmdv, int &numw, char *zeile);
void ausfuehren(char **cmdv, int numwords);
int cd(char *);
void info(char **cmdv);
void listdir(char **cmdv);
void setpath (char **cmdv);
void envir (char **cmdv);
void rname(char **cmdv);
int isRestrictedShell(char *argv);
int isLoginShell(char *argv);
int paramExists(string param, int argc, char **argv);
int main(int argc, char **argv, char **envp)
{
char zeile[MAXLINE]; //wir schreiben hier ein C Programm, das nur die C++ IO verwendet
char *cmdv[MAXWORDS];
int numw;
bool hg;
FILE *pfile;
int shellIsRestriced = 0;
int shellIsLoginShell = 0;
//Vergleich auf login Shell
if (isLoginShell(*argv))
{
cout << "Bin ne login shell" << endl;
//von HOME in einen String schreiben
home = getenv("HOME");
//.profile im String anhaengen
home = home+"/.profile";
//String casten auf char Pointer
p = home.c_str();
if ((pfile = fopen(p, "r"))==NULL)
{
cout << "Die Datei kann nicht geöffnen werden" << endl;
}
else
{
cout << ".profile wurde geoeffnet" << endl;
void ausfuehren(char **cmdv, int numwords)
{
int pid, ret;
if (!strcmp(cmdv[0],"ende"))
exit(numwords>1 ? atoi(cmdv[1]) : 0);
switch(pid=fork())
{
case 0: //bin der Sohn der die Hackn macht
execvp(cmdv[0], cmdv);
//perror ("cannot start programm: ");
exit(1);
break;
case -1:
perror ("cannot fork: ");
break;
default:
//cout << "Ich bin die Vatershell und hab einen Sohn mit Arbeit versorgt" << endl;
waitpid(pid, &ret, 0);
break;
}
}
int cd(char *path)
{
//wenn cd nichts uebergeben wird
if(path == 0)
//dann soll er ins HOMEDIR wechseln
//getenv = holt eine Environmentvariable
return chdir(getenv("HOME"));
//sonst, in das angebene Verzeichnis wechseln
return chdir(path);
}
void listdir(char **cmdv)
{
char *buf;
struct stat sb;
struct dirent *eintrag;
DIR *dir;
string time;
struct passwd *pwd;
struct group *grp;
//Formatierung der 1. Zeile
cout << left << setw(30) << "Dateiname" << left << setw (34) << "Besitzer/Gruppe" << left << setw (20) << "Typ" << setw (30) << "Statuschange" << "Rechte" << endl;
//wenn kein Parameter mit uebergeben wird
if(cmdv[1]==NULL)
{
buf = new char[50];
//dann nimm das aktuelle DIR
getcwd(buf,200);
//dir Pointer der aufs aktuelle Verzeichnis zeigt
dir=opendir(buf);
}
else
{
//sonst nimm das angegeben Verzeichnis
buf=cmdv[1];
//dir Pointer der aufs aktuelle Verzeichnis zeigt
dir=opendir(buf);
}
//Jede Datei durchgehen
for(eintrag = readdir(dir); eintrag !=NULL; eintrag = readdir(dir))
{
stat(eintrag->d_name, &sb);
//Dateiname ausgeben
cout << left << setw(30) << eintrag->d_name;
//Benutzer und Gruppe
//Benutzer
//File oeffnen (die passwd) mit Leserechten
pwd = getpwuid(sb.st_uid);
cout << pwd->pw_name;
//Gruppe
//File oeffnen (die group) mit Leserechten
//hier auf Solaris, da er keine ID finden wuerde in der /etc/group
grp = getgrgid(sb.st_gid);
if(grp != NULL)
{
cout << "/" << left << setw(26) << grp->gr_name;
}
else
{
cout << "/" << left << setw(26) << "unbekannt";
}
if(stat(buf,&sb)==-1)
{
cout << "Fehler: stat ";
exit(EXIT_FAILURE);
}
//Typ ueberpruefung und ausgeben
switch (eintrag->d_type)
{
case 0:
cout << setw(20) << "Unknown";
break;
case 1:
cout << setw(20) << "FIFO";
break;
case 2:
cout << setw(20) << "Character";
break;
case 4:
cout << setw(20) << "Verzeichnis";
break;
case 6:
cout << setw(20) << "Block";
break;
case 8:
cout << setw(20) << "Datei";
break;
case 10:
cout << setw(20) << "Verweis";
break;
case 12:
cout << setw(20) << "Socket";
break;
default:
cout << setw(20) << "sonstiges";
break;
}
//letzte Statusaenderung
time=ctime(&sb.st_ctime);
time=time.substr(0,time.find_last_of("\n"));
cout << left << setw(30) << time;
//Abfrage ob es ein File (regular) File ist, und ob es angegeben worden ist
if(!(stat(cmdv[1],&file) == 0 && S_ISREG(file.st_mode)) || (cmdv[1] == NULL))
{
cout << "Das angegebene ist kein FILE!" << endl;
return;
}
//Abfrage ob ein neuer Name angegeben worden ist
else if(cmdv[2]==NULL)
{
cout << "Es wurde kein File(neu) angegeben" << endl;
return;
}
//falls das umbenennen nicht funktioniert hat
else if(rename(cmdv[1], cmdv[2])== -1)
{
cout << "Hat nicht funktioniert!" << endl;
return;
}
}
void setpath (char **cmdv)
{
char *pPath;
char a[100];
string str, gesamt;
const char *p;
struct stat dir;
int i=0;
//holt den Inhalt der Pfadvariblen
pPath = getenv("PATH");
//solange man nicht "ende" eingibt
while(str != "ende")
{
//lese die Zeile ein
getline(cin,str);
//wenn der String nicht "ende" ist
if (str != "ende")
{
//wenn der String nicht mit / beginnt
if(str.find_first_of("/"))
{
cout << "Kein Pfad" << endl;
}
else
{
//stat und string casten
stat(str.c_str(),&dir);
int isRestrictedShell(char *argv)
{
int i;
// i auf die Laenge von argv[0] (Programmname) setzen
i = strlen(argv);
// Von hinten nach vorne die Zeichen vergleichen und nach / suchen
while (argv[i] != '/' && i >= 0)
{
// Zaehler verkleinern
i--;
}
// 1. Zeichen nach / oder Stringanfang nehmen
i++;
//ueberpreuft ob - uebergeben wurde
if(*argv =='-')
{
return 1;
}
return 0;
}
int paramExists(string param, int argc, char **argv)
{
//for Schleife die auf den angehaengten String ueberpreuft
for (int i = 0; i < argc; ++i)
{
if(strcmp(argv[i],param.c_str())==0)
return 1;
}
return 0;
}
nun zur Frage was macht er falsch könnt Ihr vielleicht Helfen wäre echt nett Danke