Shellskript aus einem C++ Programm starten



  • Hi an alle,

    ich möcht aus meinem C++ Programm, das auf einem Windows 2000 Rechner läuft, ein Shellskript auf einer Unix-Maschine Starten.
    Normalerweise mach ich das über Telnet, würde es aber gerne durch ein C++ Programm ablösen.
    Kann mir da bitte einer helfen. Ich habe den Borland Builder 5 Enterprise bei mir installiert.

    Dank an alle die sich meiner erbarmen.

    Gruß

    Bountblasher



  • Wie machst du es denn über Telnet? Du könntest dich nämlich per WinSock (oder den WinSock-Wrapping-Klassen für C++Builder) mit dem Telnet-Server über Port 25 verbinden und dann deinen Befehl senden. Ich könnte dir einen Code posten, über den du dich mit WinSock mit einem Server verbinden kannst.



  • Hallo WebFritzi,

    ich öffne eine Telnetsitzung und melde mich mit Benutzername und Passwort an der Unix-Maschine an. Dann wechsle ich in das Verzeichnis Skripte (cd Skripte), wo mein Shellskripte liegen und starte es (dvl_bk.sh).
    Wäre nett, wenn du mir ein Beispiel posten könntest, da ich noch nie mit dem WinSock gearbeitet hab.



  • Tja, erstmal sagst du mir, welche Befehle dafür in Telnet erforderlich sind. Ich muss schließlich wissen, was ich senden muss. Protokolliere einfach die gesamte Telnet-Sitzung einschließlich des Startens des Skriptes.

    P.S.: Wo erfolgt denn die Ausgabe des Skriptes? Läuft das dann auch in der Telnet Umgebung (in der Konsole) oder auf deinem Compi?



  • Ich gehe wie folgt vor:

    Start->Ausführen->Telnet

    Im Fenster das aufgeht ->
    open user/pw@unix (Unix ist der Rechner auf dem die Skripte liegen)
    cd Skripte (der Ordner auf dem Unix Rechner)
    dvl_bk.sh (das zustartende Skript)

    P.S.: Das Skript läuft selbstständig. Nach dem letzten Befehl (dvl_bk.sh) kann die Verbindung getrennt werden.



  • Du könntest natürlich auch einfach die Telnet-Komponente aus dem Indy-Paket benutzen ...



  • Gibt es da nicht irgendeine einfache Möglichkeit mit dem Borland Builder?
    Die Idee vom WebFritzi wäre mir am liebsten, aber ich brauchte ein Beispiel, da ich mit dieser Komponente noch nie gearbeitet hab.



  • Bountblasher schrieb:

    Start->Ausführen->Telnet

    Das kenne ich noch garnicht. Ich mache das immer in der Eingabeaufforderung: telnet smtp.web.de 25[Return]. Wieder was neues gelernt. 😉

    Bountblasher schrieb:

    Im Fenster das aufgeht ->
    open user/pw@unix (Unix ist der Rechner auf dem die Skripte liegen)

    Kommt da denn dann auch ein Trennstrich (/) zwischen deinen Usernamen und das Passwort (z.B. "open WebFritzi/MyPass@unix")? Beantworte mir nur noch diese Frage. Dann kann ich dein Problem bearbeiten.

    Bountblasher schrieb:

    P.S.: Das Skript läuft selbstständig. Nach dem letzten Befehl (dvl_bk.sh) kann die Verbindung getrennt werden.

    Kann die Verbindung sofort getrennt werden durch ein "quit"?

    P.S.: Ach ja, wichtig: der Hostname des Computers, mit dem du dich verbinden willst, ist "unix", ja? Stimmt das so? Das Problem ist: mit was soll verbunden werden? Mit "unix" oder mit "user/pw@unix"? Was heißt dieses @-Zeichen in diesem Zusammenhang? Der Hostname ist "unix", OK, aber welchen Hostnamen muss ich in meine Connect-Funktion reinschreiben? "unix" oder "user/pw@unix"?



  • Bountblasher schrieb:

    Gibt es da nicht irgendeine einfache Möglichkeit mit dem Borland Builder?

    Einfacher als mit einer Komponente geht's wohl kaum. Und unter den Beispielen gibt es auch ein Telnetclient-Projekt.
    Aber ich sehe schon, du bist auf eine Fix-Fertig-Lösung aus. Denn sonst könntest du WebFritzis Vorschlag, ohne irgendwelche Details zu kennen, ja wohl kaum von vonherein bevorzugen.



  • Ja UNIX ist der PC mit dem ich mich verbinden will.
    Es würde reichen, wenn in der Connect-Funktion UNIX steht.
    Das @ zeigt nur an, das jetzt der PC kommt mit dem man sich verbinden will genau so wie der / den Benutzernamen vom Passwort trennt.

    PC: UNIX
    Benutzer: user
    Passwort: pw

    @Jansen Nein ich bin nicht nach auf eine Fix und fertigen Lösung aus, aber ich würde mir das Beispiel von WebFritzi gerne mal anschauen. Bin auch nebenbei beim ausprobieren deines Tipps.

    Danke schon mal im voraus für eure Hilfe.



  • Bountblasher schrieb:

    Das @ zeigt nur an, das jetzt der PC kommt mit dem man sich verbinden will genau so wie der / den Benutzernamen vom Passwort trennt.

    PC: UNIX
    Benutzer: user
    Passwort: pw

    Könntest du dich bitte klarer ausdrücken? Du gehst davon aus, dass ich mich mit Telnet auskenne - tu ich aber nicht. Wenn ich nun nur "UNIX" in meine Connect-Funktion schreibe, wie soll ich dann den Benutzer und den Pass angeben??? Sagen wir, du schreibst in telnet nur "open UNIX". Musst du danach dann den Benutzernamen und das Passwort angeben? Wenn ja, wie? Nacheinander? Bitte um ein fiktives Protokoll.



  • Ich gebe in der Eingabeaufforderung folgendes ein:

    Telnet UNIX [Enter]

    dann kommt die Ausgabe:
    HP-UX UNIX B.11.11 U 9000/800 (tm)

    login: user [Enter]
    Password: pw [Enter]

    login und Password ist eine Ausgabe vom System, ich gebe nur den
    Benutzer "user" und das Passwort "pw" ein.



  • Bis Fritzi so in die Puschen kommt (;)) zeige ich mal eine Indy-Variante.

    //---------------------------------------------------------------------------
    bool logged_in = false;
    String strLogin    = "jansen";
    String strPass     = "pass";
    String strCommand  = "touch test.dat;ls -l test.dat;rm test.dat";
    String strResponse = "-rw-r--r--    1 jansen   users";
    String strText = "";
    
    void __fastcall TForm1::TN1DataAvailable(TIdTelnet *Sender,
          const AnsiString Buffer)
    {
      if (Buffer != "")
      {
        if (!logged_in)
        {
          // Loginprompt erreicht
          if (UpperCase(Buffer).Pos("LOGIN: ") == Buffer.Length() - 6)
            strText = strLogin;
          // Passwortprompt erreicht
          else if (UpperCase(Buffer).Pos("PASSWORD: ") == Buffer.Length() - 9)
            strText = strPass;
          // Eingabeprompt erreicht, Kommando abschicken
          else if (UpperCase(Buffer).Pos(UpperCase("jansen@suse82:~> ")) > 0)
          {
            logged_in = true;
            strText = strCommand;
          }
        }
        // Ausgabe enthält erwartetes Resultat des Kommandos, Verbindung beenden
        else if (UpperCase(Buffer).Pos(UpperCase(strResponse)) > 0)
          strText = "exit &";
    
        if (strText != "")
        { // das eigentliche Versenden
          for (int i = 1; i <= strText.Length(); i++)
            TN1->SendCh(strText[i]);
          TN1->SendCh(VK_RETURN);
          strText = "";
        }
    
        // Kontrollausgabe
        Memo1->Lines->Add(Buffer);
      }
    }
    //---------------------------------------------------------------------------
    

    TN1 ist die Indy-Komponente, Connect und Disconnect erfolgen im Formkonstruktor/-destruktor.



  • Wie bau ich die Verbindung auf? So was wie Active oder open hab ich nicht gefunden.



  • Jansen schrieb:

    ... Connect und Disconnect ...

    Wie könnten entsprechende Methoden wohl heissen? 😉



  • Hab ich dann auch gemerkt 🙄 *schäm*

    Noch mal eine Frage zu deinem Beispiel.
    Ich hab alles so gemacht wie du, funktioniert auch wunderbar.
    Mein Unix sieht ein bisschen anders aus als deins, wie kann ich bei mir prüfen in was für einem Verzeichnis ich mich befinde?

    user@UNIX [/user/dwhst/]
    $

    Bei dem $ Zeichen kann ich meinen Code eingeben. Hab dein Skript schon so abgeändert, das ich in das () Verzeichnis wechseln kann.
    Wie kann ich aber abfragen ob ich wirklich in diesem Verzeichnis bin?



  • Du musst halt immer den letzten Output auswerten (die Buffer-Variable). Da steht das drin, was du auch nach einer Eingabe an der Konsole sehen würdest.



  • Bountblasher schrieb:

    Ich gebe in der Eingabeaufforderung folgendes ein:

    Telnet UNIX [Enter]

    dann kommt die Ausgabe:
    HP-UX UNIX B.11.11 U 9000/800 (tm)

    login: user [Enter]
    Password: pw [Enter]

    login und Password ist eine Ausgabe vom System, ich gebe nur den
    Benutzer "user" und das Passwort "pw" ein.

    Wenn du das gleich so gesagt hättest, dann hätte ich dir sofort diesen Code geben können:

    #include <windows.h>
    #include <winsock2.h>
    //---------------------------------------------------------------------------
    
    VOID ErrorMsg(HWND hwnd, LPCTSTR lpszErr)
    {
          MessageBox(hwnd, TEXT(lpszErr), TEXT("ERROR"), MB_OK | MB_ICONERROR);
    }
    //---------------------------------------------------------------------------
    
    VOID ShowLastSockError(HWND hwnd)
    {
        LPVOID lpMsgBuf;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                      FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL,
                      WSAGetLastError(),
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                      (LPTSTR)&lpMsgBuf,
                      0,
                      NULL);
    
           MessageBox(hwnd, (LPCTSTR)lpMsgBuf, TEXT("ERROR"), MB_OK | MB_ICONERROR);
    
        LocalFree(lpMsgBuf);
    }
    //---------------------------------------------------------------------------
    
    BOOL StartWinsock2()
    {
       WSADATA wsa;
       int retVal = WSAStartup(MAKEWORD(2,0), &wsa);
    
       if(retVal == 0)
          return TRUE;
       else
       {
          switch(retVal)
          {
             case WSASYSNOTREADY:
                ErrorMsg(NULL, "The underlying network subsystem is not ready for network communication");
    
             case WSAVERNOTSUPPORTED:
                ErrorMsg(NULL, "The version of Windows Sockets support requested (2.0) is not provided by this particular Windows Sockets implementation.");
    
             case WSAEINPROGRESS:
                ErrorMsg(NULL, "A blocking Windows Sockets 1.1 operation is in progress");
    
             case WSAEPROCLIM:
                ErrorMsg(NULL, "Limit on the number of tasks supported by the Windows Sockets implementation has been reached");
    
             case WSAEFAULT:
                ErrorMsg(NULL, "Implementation Error");
          }
          return FALSE;
       }
    }
    //---------------------------------------------------------------------------
    
    BOOL EndWinsock2()
    {
       if( WSACleanup() == 0 )
          return TRUE;
       else
       {
          ShowLastSockError(NULL);
          return FALSE;
       }
    }
    //---------------------------------------------------------------------------
    
    SOCKET SetupSocket()
    {
       SOCKET retVal = socket(AF_INET, SOCK_STREAM, 0);
       if(retVal != INVALID_SOCKET)
          return retVal;
       else
       {
          ShowLastSockError(NULL);
          return NULL;
       }
    }
    //---------------------------------------------------------------------------
    
    BOOL CloseSocket(SOCKET s)
    {
       if( closesocket(s) == 0 )
          return TRUE;
       else
       {
          ShowLastSockError(NULL);
          return FALSE;
       }
    }
    //---------------------------------------------------------------------------
    
    ULONG GetIPFromString(LPTSTR hostnameOrIp)
    {
      ULONG retVal;
      HOSTENT* he;
    
      // Is there an IP in hostnameOrIp?
      retVal = inet_addr(hostnameOrIp);
    
      if(retVal == INADDR_NONE)       // NO
      {
        he = gethostbyname(hostnameOrIp);
        if(he == NULL)                // An error occured
        {
          ShowLastSockError(NULL);
          return 0;
        }
        else                          // everything's fine
          // Copy the first 4 bytes from he->h_addr_list to ip
          CopyMemory((PVOID)&retVal, he->h_addr_list[0], 4*sizeof(BYTE));
      }
    
      return retVal;
    }
    //---------------------------------------------------------------------------
    
    BOOL Connect(SOCKET s, USHORT port, LPCTSTR lpszCompName)
    {
       SOCKADDR_IN addr;
       int rc;
    
       ZeroMemory((PVOID)&addr, sizeof(SOCKADDR_IN));
       addr.sin_family = AF_INET;
       addr.sin_port = htons(port);
       addr.sin_addr.s_addr = GetIPFromString((LPTSTR)lpszCompName);
    
       rc = connect(s, (SOCKADDR*)&addr, sizeof(SOCKADDR));
       if(rc == 0)
          return TRUE;
       else
       {
          ShowLastSockError(NULL);
          return FALSE;
       }
    }
    //---------------------------------------------------------------------------
    
    BOOL Send(SOCKET s, char* buf, int len)
    {
       int bytes_sent;
    
       bytes_sent = send(s, buf, len, 0);
       if(bytes_sent != SOCKET_ERROR)
          return TRUE;
       else
       {
          ShowLastSockError(NULL);
          return FALSE;
       }
    }
    //---------------------------------------------------------------------------
    
    BOOL Receive(SOCKET s, char* buf, int len)
    {
       int bytes_received;
    
       bytes_received = recv(s, buf, len, 0);
       if(bytes_received != SOCKET_ERROR)
          return TRUE;
       else
       {
          ShowLastSockError(NULL);
          return FALSE;
       }
    }
    //---------------------------------------------------------------------------
    
    #pragma argsused
    WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
       SOCKET s;
       char buf[4096];
    
       if( !StartWinsock2() )
          return 0;
    
       s = SetupSocket();
       if(s == NULL)
       {
          EndWinsock2();
          return 0;
       }
    
       if( !Connect(s, 23, "UNIX") )
       {
          CloseSocket(s);
          EndWinsock2();
          return 0;
       }
       Receive(s, buf, 4096);
    
       Send(s, "Username\r\n", 10);
       Receive(s, buf, 4096);
    
       Send(s, "Pass\r\n", 6);
       Receive(s, buf, 4096);
    
       Send(s, "cd Skripte\r\n", 12);
       Receive(s, buf, 4096);
    
       Send(s, "dvl_bk.sh\r\n", 11);
       Receive(s, buf, 4096);
    
       Send(s, "quit\r\n", 6);
       Receive(s, buf, 4096);
    
       if( !CloseSocket(s) )
       {
          EndWinsock2();
          return 0;
       }
    
       if( !EndWinsock2() )
          return 0;
    
       return 1;
    }
    //---------------------------------------------------------------------------
    

    Der Code ist nicht getestet, da ich keinen Server kenne, bei dem man Benutzername und Pass eingeben muss. Ich hoffe, du kannst auch damit ein wenig was anfangen.



  • äh ja, schön, Fritzi... nur sind wir hier im VCL Forum und haben glücklicherweise den TClientSocket, welcher uns n Haufen von dem WinAPI-Gewusel da abnimmt und die Verwendung von Sockets erheblich erleichtert (o;

    -junix



  • junix schrieb:

    äh ja, schön, Fritzi... nur sind wir hier im VCL Forum und haben glücklicherweise den TClientSocket, welcher uns n Haufen von dem WinAPI-Gewusel da abnimmt und die Verwendung von Sockets erheblich erleichtert (o;

    Ja schön, junix. Aber du hast wohl mitbekommen, dass Bountblasher mich darum gebeten hatte, ihm etwas Code zu geben. Wenn nicht, dann lies den Thread nochmal durch. Tut dir gut! Ich habe somit lediglich mein Versprechen eingelöst. Ganz davon abgesehen arbeitet deine tolle Klasse sicher auch mit genau den Funktionen, mit denen ich arbeite. Warum sollte Bountblasher nicht wissen, was wirklich dahinter steckt. Und nochmal außerdem: ich habe diese Klasse nicht, denn ich hab den BCB3.


Anmelden zum Antworten