Visualizzazione dei risultati da 1 a 8 su 8
  1. #1
    Utente di HTML.it
    Registrato dal
    Nov 2012
    Messaggi
    18

    [C/C++] Simulazione della shell di Linux ( ubuntu )

    Salve,
    Mi e' stato assegnato come compito di simulare la shell del terminale di Linux ( ho come distribuzione ubuntu 12.10 ). Quindi devo fare un while infinito in cui ricevo in ingresso una stringa e poi , distingudendo il comando dai parametri, devo chiamare una fork in cui il figlio dovrà fare una exec con il comando ricevuto e i parametri passati ( lo devo fare così da esercizio ). Il programma che ora incollerò è funzionante o, per meglio dire, funziona con certi comandi del tipo "ps -aux" , "ss" , "free" , free -m , ec.. però non li esegue tutti:
    - se scrivo "ls" vai in errore
    - se scrivo "help" non lo esegue come se non trovasse il comando
    e altri comandi
    Il problema molto probabilmetne è che non conosco il percorso dove si trovano questi comandi perchè io ho messo come unici percorsi /bin/comando e /usr/bin/comando. Potreste dirmi se ci sono altre cartelle dove stanno i comandi da terminale o se c'è un modo diverso , un approccio diverso per utilizzare l' exec? grazie mille

    codice:
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <iostream>
    
    typedef char Stringa[100];
    
    int DistinguiComando(char*);
    void DistinguiParametri(int*,char**);
    void Path(char* path,char* comando,char *);
    
    int DistinguiComando( char * comando){
        char lettera;
        int pos = 0;
        int continuo = 0;
        while ( (lettera = getchar() ) != '\n' ) {
              continuo = 1;
              if (lettera == ' ')
                          break;
              else
                   comando[pos++] = lettera;
        }
        if ( continuo == 1 && lettera == '\n' ){
           comando[pos] = '\0';
           return 2;
        }
        if (continuo == 1) {
           comando[pos] = '\0';
           return 1;
        }
        else
             return 0;
    }
    
    void DistinguiParametri ( int *dimensione, char ** par ) {
         char lettera;
         *dimensione = 0;
         int nextChar = 0;
         par[(*dimensione)] = new char[15];
         while ( (lettera=getchar() ) != '\n') {
               if (lettera == ' '){
                  par[*dimensione][nextChar] = '\0';
                  nextChar = 0;
                  (*dimensione)++;
                  par[(*dimensione)] = new char[15];
               }
               else {
                    par[(*dimensione)][nextChar] = lettera;
                    nextChar++;
               }
         }
         (*dimensione)++;
    }
    
    /*void DistinguiParametri(int *dimensione, char* par) {
         char lettera;
         int pos = 0;
         while ( (lettera = getchar()) != '\n' ) {
               par[pos++] = lettera;
         }
    }*/
    
    void Path(char* path,char* comando,char* percorso) {
         int pos=0;
         int i = 0 ;
         while ( percorso[i] != '\0' )
    	path[pos++] = percorso[i++];
         i= 0;
         while ( comando[i] != '\0' )
               path[pos++] = comando[i++];
         path[pos] = '\0';
               
         
    }
    
    
    int main()
    {
        int pid;
        while (1) {
            char *comando = new char[15];
            char ** parametri = new char*[10];
            char *path = new char[100];
    	char *percorso = new char[100];
            int *dimensione = new int;
            int verifica = DistinguiComando(comando);
            if ( verifica == 1) {
                 DistinguiParametri(dimensione,parametri);
                 pid = fork();
                 if ( pid == 0) {
    		  strcpy(percorso,"/bin/");
                 	  Path(path,comando,percorso);
                      if ( execv(path,parametri) == -1 ){
    		  	strcpy(percorso,"/usr/bin/");
    			Path(path,comando,percorso);
    			if ( execv(path,parametri) == -1 ) {
    				strcpy(percorso,"/usr/lib/apt/solvers/");
    				Path(path,comando,percorso); 
    				if ( execv(path,parametri) == -1 ) {
    					strcpy(percorso,"/usr/lib/");
    					Path(path,comando,percorso); 
    					execv(comando,parametri);
    				}
    			}
    
    		  }
                      _exit(0);
                 }
                 else
                      wait(NULL);
            }
            else if ( verifica == 2 ) {
                 pid = fork();
                 if ( pid == 0) {
    		  strcpy(percorso,"/bin/");
                 	  Path(path,comando,percorso);
                      if ( execv(path,parametri) == -1 ){
    		  	strcpy(percorso,"/usr/bin/");
    			Path(path,comando,percorso);
    			if ( execv(path,parametri) == -1 ) {
    				strcpy(percorso,"/usr/lib/apt/solvers/");
    				Path(path,comando,percorso); 
    				if ( execv(path,parametri) == -1 ) {
    					strcpy(percorso,"/usr/lib/");
    					Path(path,comando,percorso); 
    					execv(comando,parametri);
    				}
    			}
    
    		  }
                      _exit(0);
                 }
                 else
                      wait(NULL);
            }
        }
         
        return 0;
    }

  2. #2
    Utente di HTML.it
    Registrato dal
    Jul 2008
    Messaggi
    1,326

    Re: [C/C++] Simulazione della shell di Linux ( ubuntu )

    Originariamente inviato da ITobeI
    - se scrivo "ls" vai in errore
    (la prossima volta riporta anche l'errore) se ti riferisci a questo:

    codice:
    A NULL argv[0] was passed through an exec system call.
    dipende dal fatto che il secondo argomento della execv ("parametri") ha, come prima stringa, una stringa nulla, mentre invece (per la documentazione) dovrebbe essere il nome del file dell'eseguibile, cioè nel caso specifico "ls", ma non imposti tale valore da nessuna parte se segui bene tutto il flusso del programma dal momento in cui scrivi "ls" al momento in cui viene eseguita la syscall.

    Originariamente inviato da ITobeI
    - se scrivo "help" non lo esegue come se non trovasse il comando
    e infatti non lo trova. Non confondere i comandi di sistema con i comandi built-in della bash: "help" appartiene a questa seconda categoria (tant'è che se provi ad avviare quel comando da csh, per dirne una, ottieni "command not found"), pertanto la syscall exec* non potrà mai eseguirtelo (non direttamente, di sicuro).

    Originariamente inviato da ITobeI
    e altri comandi
    Il problema molto probabilmetne è che non conosco il percorso dove si trovano questi comandi perchè io ho messo come unici percorsi /bin/comando e /usr/bin/comando. Potreste dirmi se ci sono altre cartelle dove stanno i comandi da terminale o se c'è un modo diverso , un approccio diverso per utilizzare l' exec? grazie mille
    è inutile complicare il codice in quel modo per ottenere il path del comando: provare i "path possibili" uno dietro l'altro è una soluzione brutta, inefficiente, non universale e assolutamente non scalabile (tra le altre cose, un path come /usr/lib/apt/solvers/ sarà presente solo su distro linux che usano APT come gestore di pacchetti, ma il mondo là fuori è molto più vasto).
    Semplifica.
    Anziché usare la funzione execv(), ricorri a execvp() che accetta come primo parametro il semplice nome dell'eseguibile (non il path completo), sobbarcandosi poi il compito di cercarlo in automatico in tutte le directory presenti nella variabile di ambiente PATH, svincolandoti quindi completamente dall'onere di dover comporre il percorso a mano.
    man exec per maggiori informazioni su tutte le funzioni della famiglia exec*.
    every day above ground is a good one

  3. #3
    Utente di HTML.it
    Registrato dal
    Nov 2012
    Messaggi
    18
    codice:
    la prossima volta riporta anche l'errore) se ti riferisci a questo:
    Scusami hai ragione ^^
    Il problema nasce nel come ho fatto la lettura, se scrivessi soltanto "ls" poi mi chiederebbe sempre di scriver input allora ho dovuto creare due return nella funzione DistinguiComando ( 2 se esiste solo una parola cioè il comando e quindi non chiama DistinguiParametri , e 1 se ci sono anche i parametri, 0 invece se non c'è neanche il comando ). Nella funzione execvp ( o anche execv ) devo comunque passargli un char* const char* oltre al const char* anche se devo passarli solo il comando. Ho capito quello che hai detto ma al livello implementativo dovrei fare così? ( sotto la parte del codice interessata )
    codice:
    int verifica = DistinguiComando(comando);
            if ( verifica == 2) {
                 pid = fork();
                 if ( pid == 0) {
                    strcpy(parametri[*dimensione],comando);
    		execvp (comando,parametri);
                 }
              ......
    ma quindi anche quando gli passo dei parametri ( es.: "ps -aux" ) la lista dei parametri deve essere formato da : 1) ps 2) -aux ... oppure solo da -aux? Se non è proprio così allora non ho capito xD


    codice:
    e infatti non lo trova. Non confondere i comandi di sistema con i comandi built-in della bash: "help" appartiene a questa seconda categoria (tant'è che se provi ad avviare quel comando da csh, per dirne una, ottieni "command not found"), pertanto la syscall exec* non potrà mai eseguirtelo (non direttamente, di sicuro).
    è inutile complicare il codice in quel modo per ottenere il path del comando: provare i "path possibili" uno dietro l'altro è una soluzione brutta, inefficiente, non universale e assolutamente non scalabile (tra le altre cose, un path come /usr/lib/apt/solvers/ sarà presente solo su distro linux che usano APT come gestore di pacchetti, ma il mondo là fuori è molto più vasto).
    Semplifica.
    Anziché usare la funzione execv(), ricorri a execvp() che accetta come primo parametro il semplice nome dell'eseguibile (non il path completo), sobbarcandosi poi il compito di cercarlo in automatico in tutte le directory presenti nella variabile di ambiente PATH, svincolandoti quindi completamente dall'onere di dover comporre il percorso a mano.
    man exec per maggiori informazioni su tutte le funzioni della famiglia exec*.
    Questo ho capito e sei stato molto esaustivo infatti modificando con execvp mi funziona allo stesso modo e mi ha semplificato di gran lunga il codice .

    Comunque sia ti ringrazio di tutto il tempo ^^

  4. #4
    Utente di HTML.it
    Registrato dal
    Jul 2008
    Messaggi
    1,326
    Per quotare usa il tag "quote", non "code".

    Scusami hai ragione ^^
    Il problema nasce nel come ho fatto la lettura, se scrivessi soltanto "ls" poi mi chiederebbe sempre di scriver input allora ho dovuto creare due return nella funzione DistinguiComando ( 2 se esiste solo una parola cioè il comando e quindi non chiama DistinguiParametri , e 1 se ci sono anche i parametri, 0 invece se non c'è neanche il comando ). Nella funzione execvp ( o anche execv ) devo comunque passargli un char* const char* oltre al const char* anche se devo passarli solo il comando. Ho capito quello che hai detto ma al livello implementativo dovrei fare così? ( sotto la parte del codice interessata )
    supponendo di voler ricorrere alla execvp() per i motivi di cui sopra (sgravarsi dall'onere di costruire a mano il path dell'eseguibile), nel caso della ls la execvp dovrebbe essere richiamata con qualcosa del genere:

    codice:
    	char *params[] = {"ls", NULL};
            ...
    	execvp("ls", params);
    il primo argomento è il nome del file da eseguire (per intenderci, nella directory /bin hai un vero e proprio file con nome "ls" che è eseguibile e contiene il codice binario del comando omonimo); il fatto che tu ricorra alla funzione exec* con la p fa sì che tale file venga cercato in tutte le directory elencate nella variabile d'ambiente PATH (quindi, di norma, anche in /bin). Questo però non basta. il vettore di stringhe che segue deve contenere l'intera command line del processo, cioè l'elenco delle stringhe che scriveresti da shell per eseguire il comando (quindi il nome del programma stesso più eventuali flag): se volessi eseguire "ls -l", il codice di sopra diverrebbe:

    codice:
    	char *params[] = {"ls", "-l", NULL};
            ...
    	execvp("ls", params);
    il fatto che il primo argomento del vettore debba essere il nome stesso del file da eseguire è in realtà una convenzione, come si legge nella man page:

    The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the
    new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
    rompere una convenzione è possibile? Certo, ma se poi non funziona niente non c'è di che meravigliarsi.

    Detto questo, nel tuo programma devi assicurarti che il vettore di stringhe che passi alla execvp abbia sempre, come primo elemento, il nome del programma da eseguire, anche quando lo vuoi richiamare senza argomenti; devi inoltre assicurarti che sia NULL terminated. Nel tuo caso, quando si verifica la condizione "verifica == 2", "parametri" è l'indirizzo di base di un vettore di 10 puntatori a char che però non sono mai stati inizializzati (non mi pare che lo standard C++ garantisca che la memoria allocata con new sia azzerata, di default) e che quindi puntano un po' dove gli pare in memoria. Devi assicurarti che quel vettore abbia il nome del programma come primo elemento e NULL come ultimo, con tutte le eventuali opzioni nel mezzo.
    every day above ground is a good one

  5. #5
    Utente di HTML.it
    Registrato dal
    Nov 2012
    Messaggi
    18
    Grazie mille per la spiegazione dettagliata

    Questo mi semplifice ulteriormente le cose perchè ho creato una funziona solo per distinguere il comando dai parametri invece posso benissimo usare solo la funzione DistinguiParametri e quando chiamo la execvp gli passo come nome comando parametri[0], e come array parametri. Però devo creare la funziona che inizializzi l' array ^^.

    Volevo vedere come implementare anche il pipe ma penso che sia un qualcosa di più complesso perchè è come se dovessi fare due processi utilizzando solo una command line.

    Grazie mille vedrò di risolvere il problema.

  6. #6
    Utente di HTML.it
    Registrato dal
    Jul 2008
    Messaggi
    1,326
    Originariamente inviato da ITobeI
    Volevo vedere come implementare anche il pipe ma penso che sia un qualcosa di più complesso perchè è come se dovessi fare due processi utilizzando solo una command line.
    C'è tutto un mondo dietro. Per gradire:

    http://users.lilik.it/~mirko/gapil/g...4-23500012.1.1
    http://users.lilik.it/~mirko/gapil/gapilsu181.html

    ma sono argomenti da studiare a dovere, non si può improvvisare.
    every day above ground is a good one

  7. #7
    Utente di HTML.it
    Registrato dal
    Nov 2012
    Messaggi
    18
    Vabbè per ora il pipe può attendere però grazie per il link leggerò quando avrò tempo ^^

    Ora io dovrei, se possibile, implementare il comando & ( non so se si può chiaare comando ) , allora ho visto che la & serve per far lanciare un programma in background o.O il discorsoè questo:

    Ho visto un pò su interneti la funzionalità di & e l' ho provato su ubuntu e scrivendo : programma & > /dev/null <--- non me lo apre in background ( background significa hce si apre il programma ma il programma principale resta lo stesso la shell? ). Vorrei prima capire bene il comando e poi dovrei implementarlo nel simulatore della shell ( avevo pensato di fare due fork una dentro l' altra , figlio nel figlio ) .

  8. #8
    Utente di HTML.it
    Registrato dal
    Jul 2008
    Messaggi
    1,326
    Originariamente inviato da ITobeI
    Ho visto un pò su interneti la funzionalità di & e l' ho provato su ubuntu e scrivendo : programma & > /dev/null <--- non me lo apre in background ( background significa hce si apre il programma ma il programma principale resta lo stesso la shell? ). Vorrei prima capire bene il comando e poi dovrei implementarlo nel simulatore della shell ( avevo pensato di fare due fork una dentro l' altra , figlio nel figlio ) .
    Significa semplicemente che il programma viene lanciato ma il processo padre (la shell) non aspetta la terminazione del processo figlio come avverrebbe senza &, ripresentando subito il prompt all'utente per l'immissione di un ulteriore comando. Di fatto, il processo padre non deve eseguire la chiamata wait(NULL).
    every day above ground is a good one

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.