[C/C++] ApplicationService

Dieses Thema im Forum "Projekte / Codes" wurde erstellt von terraNova, 26. April 2011 .

  1. 26. April 2011
    ApplicationService

    Hey,

    ich hab spontan ein Programm geschrieben, dass Programme wie einen Service behandelt.
    D.h. es ist möglich normale Programme, die nicht als Service gedacht sind (z.B. Server wie Teamspeak3), zu starten und zu beenden. Es wird also nichts an Windows geändert und lässt sich einfach per CMD bedienen, was so manche Arbeit ersparen dürfte, wenn etwas schnell gehen soll.

    Wie gesagt, das Programm ist spontan geschrieben, sollten euch also Fehler auffallen dürft ihr mir gerne verraten wie diese auftreten (Genauer Befehl), damit ich diese sobald wie möglich beheben kann.

    Bedienung dürfte selbsterklärend sein, wenn nicht: Fragen!

    Code:
    #include <iostream>
    #include <string>
    #include <vector>
    #include <queue>
    #include <fstream>
    #include <cstdlib>
    
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    
    #define TAS_FILE_DEFAULT "tas.db"
    
    using namespace std;
    
    enum {
     SC_NONE = 0,
     SC_START = 0x01,
     SC_STOP = 0x10,
     SC_RESTART = 0x11,
    };
    
    enum {
     SQ_NONE = 0,
     SQ_ADD,
     SQ_REMOVE,
     SQ_CONTROL,
     SQ_STATUS,
    };
    
    enum {
     SDB_SKIP = -2,
     SDB_ADD = -1,
     SDB_FIRST = 0,
    };
    
    struct tas_entry {
     char id[16];
     
     char path[256];
     char param[256];
     
     unsigned pid;
     
     void
     set_id( char* _id ) {
     strncpy(id, _id, 16);
     }
     
     void
     set_path( char* _path ) {
     strncpy(path, _path, 256);
     }
     
     void
     set_param( char* _param ) {
     strncpy(param, _param, 256);
     }
    };
    
    struct tas_task {
     int type;
     int action;
     
     tas_entry entry;
     
     tas_task(int _type = SQ_NONE)
     : type(_type), action(SC_NONE) {
     memset(&entry, 0, sizeof(tas_entry));
     }
    };
    
    struct tas_config {
     bool verbose;
     bool changes;
     
     char db[256];
     
     queue< tas_task > tasks;
     vector< pair<tas_entry, long> > services;
     
     tas_config()
     : verbose(false), changes(false) {
     strncpy(db, TAS_FILE_DEFAULT, 16);
     }
     
     void
     set_db( char* _db ) {
     strncpy(db, _db, 256);
     }
    };
    
    void
    tas_check_opt(tas_config& conf, char opt, int argc_left, char **argv);
    
    void
    tas_init(tas_config& conf);
    
    void
    tas_close(tas_config& conf); // writes db if tas_config::changes was set
    
    void
    tas_process_tasks(tas_config& conf);
    
    bool
    tas_process_task(tas_config& conf, tas_task& task);
    
    void
    tas_print_task(const tas_task& task, bool details = false);
    
    bool
    tas_service_exists(const tas_config& conf, const char* id, size_t& pos);
    
    bool
    tas_service_running(tas_entry& entry);
    
    int
    main( int argc, char *argv[] ) {
     tas_config conf;
     
     if(argc == 1) {
     tas_check_opt(conf, 'h', 0, 0);
     return EXIT_SUCCESS;
     }
     
     for(int i=1; i<argc; i++) {
     if(argv[i][0] == '-') {
     size_t len = strlen(argv[i]);
     
     for(size_t j=1; j<len; j++) {
     tas_check_opt(conf, argv[i][j], argc-i, argv+i);
     }
     }
     }
     
     tas_init(conf);
     tas_process_tasks(conf);
     tas_close(conf);
     
     return EXIT_SUCCESS;
    }
    
    void
    tas_check_opt(tas_config& conf, char opt, int argc_left, char **argv) {
     switch(opt) {
     case 'h': {
     cout << "tAppSvc <-hvarcds> [id|path] [[path|action] [param]]" << endl << endl
     << "OPTIONS: " << endl
     << "\t-h USAGE" << endl
     << "\t-v VERBOSE" << endl
     << "\t-a <id> <path> [param] ADD SERVICE" << endl
     << "\t-r <id> REMOVE SERVICE" << endl
     << "\t-c <id> <action> CONTROL SERVICE" << endl 
     << "\t-d <path> ALT. DATABASE" << endl
     << "\t-s SHOW SERVICES" << endl
     << "\t-s <id> STATUS" << endl << endl
     << "ACTIONS: " << endl
     << "\tstart START SERVICE" << endl
     << "\tstop STOP SERVICE" << endl
     << "\trestart RESTART SERVICE" << endl << endl;
     
     exit(EXIT_SUCCESS);
     } break;
     
     case 'v': {
     conf.verbose = true;
     } break;
     
     case 'a': {
     tas_task task(SQ_ADD);
     
     if(argc_left < 2) {
     cout << "Too few parameter (-a)" << endl << endl;
     
     tas_check_opt(conf, 'h', 0, 0);
     exit(EXIT_FAILURE);
     }
     
     task.entry.set_id(argv[1]);
     task.entry.set_path(argv[2]);
     
     if(argc_left > 3 && argv[3][0] != '-')
     task.entry.set_param(argv[3]);
     
     conf.tasks.push(task);
     } break;
     
     case 'r': {
     tas_task task(SQ_REMOVE);
     
     if(argc_left < 1) {
     cout << "Too few parameter (-r)" << endl << endl;
     
     tas_check_opt(conf, 'h', 0, 0);
     exit(EXIT_FAILURE);
     }
     
     task.entry.set_id(argv[1]);
     
     conf.tasks.push(task);
     } break;
     
     case 'c': {
     tas_task task(SQ_CONTROL);
     
     if(argc_left < 3) {
     cout << "Too few parameter (-c)" << endl << endl;
     
     tas_check_opt(conf, 'h', 0, 0);
     exit(EXIT_FAILURE);
     }
     
     task.entry.set_id(argv[1]);
     
     if(!strcmp(argv[2], "start")) {
     task.action = SC_START;
     } else if(!strcmp(argv[2], "stop")) {
     task.action = SC_STOP;
     } else if(!strcmp(argv[2], "restart")) {
     task.action = SC_RESTART;
     } else {
     cout << "Unknown action '" << argv[2] << "' for '" << argv[1] << "'. Skipping" << endl << endl;
     break;
     }
     
     conf.tasks.push(task);
     } break;
     
     case 'd': {
     if(argc_left < 1) {
     cout << "Too few parameter (-d)" << endl << endl;
     
     tas_check_opt(conf, 'h', 0, 0);
     exit(EXIT_FAILURE);
     }
     
     conf.set_db(argv[1]);
     } break;
     
     case 's': {
     tas_task task(SQ_STATUS);
     
     if(argc_left > 1)
     task.entry.set_id(argv[1]);
     
     conf.tasks.push(task);
     } break;
     
     default: {
     cout << "Unknown parameter '" << opt << "'. Skipping" << endl << endl;
     } break;
     }
    }
    
    void
    tas_init(tas_config& conf) {
     fstream db(conf.db, ios::binary|ios::in);
     tas_entry ent;
     size_t pos = 0;
     
     if(db.is_open() == false) {
     cout << "Database does not exists (" << conf.db << "). Creating and Quitting." << endl << endl;
     
     db.open(conf.db, ios::binary|ios::app);
     db.close();
     
     exit(EXIT_FAILURE);
     }
     
     if(db.rdbuf()->in_avail() > 1) {
     while(db.rdbuf()->in_avail() > 1) { // SURELY EOF 
     db.read((char*)&ent, sizeof(tas_entry));
     conf.services.push_back(pair<tas_entry, long>(ent, pos));
     pos = db.tellg();
     
     if(conf.verbose == true) {
     cout << "read service '" << ent.id << "'" << endl;
     }
     }
     }
     
     db.close();
    }
    
    void
    tas_close(tas_config& conf) {
     if(conf.changes == false)
     return;
     
     fstream db(conf.db, ios::binary|ios::out);
     
     if(db.is_open() == false) {
     cout << "Fatal Error: Could not write Database. Quitting." << endl;
     exit(EXIT_FAILURE);
     }
    
     for(vector< pair<tas_entry, long> >::iterator ent=conf.services.begin(); ent != conf.services.end(); ent++) {
     if((*ent).second != SDB_SKIP) {
     db.write((char*)(&(*ent).first), sizeof(tas_entry));
     } else if((*ent).second == SDB_SKIP && (ent+1) == conf.services.end()) {
     char eof = EOF;
     db.write(&eof, 1);
     }
     }
    
     db.close();
    }
    
    void
    tas_process_tasks(tas_config& conf) {
     while(conf.tasks.empty() == false) {
     
     bool ret = tas_process_task(conf, conf.tasks.front());
     
     if(ret == true) {
     cout << "Task succeeded: ";
     tas_print_task(conf.tasks.front());
     } else {
     cout << "Task failed: ";
     tas_print_task(conf.tasks.front(), true);
     }
     
     conf.tasks.pop();
     }
    }
    
    bool
    tas_process_task(tas_config& conf, tas_task& task) {
     size_t pos;
     
     switch(task.type) {
     case SQ_ADD: {
     if(tas_service_exists(conf, task.entry.id, pos) == true) {
     cout << "Service (" << task.entry.id << ") already exists. Skipping" << endl << endl;
     return false;
     }
     
     HANDLE hFile = CreateFileA(task.entry.path, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
     
     if(FAILED(hFile)) {
     cout << "Path (" << task.entry.path << ") doesn\'t exist. Skipping" << endl << endl;
     return false;
     }
     
     CloseHandle(hFile); 
     conf.services.push_back(pair<tas_entry, size_t>(task.entry, SDB_ADD));
     
     conf.changes = true;
     } break;
     
     case SQ_REMOVE: {
     if(tas_service_exists(conf, task.entry.id, pos) == false) {
     cout << "Service (" << task.entry.id << ") doesn\'t exists. Skipping" << endl << endl;
     return false;
     }
     
     cout << (*(conf.services.begin()+pos)).first.id << endl;
     conf.services.at(pos).second = SDB_SKIP;
     
     conf.changes = true;
     } break;
     
     case SQ_CONTROL: {
     if(tas_service_exists(conf, task.entry.id, pos) == false) {
     cout << "Service (" << task.entry.id << ") doesn\'t exists. Skipping" << endl << endl;
     return false;
     }
     
     switch(task.action) {
     case SC_RESTART: {
     cout << "Restarting Service (" << task.entry.id << ")" << endl;
     }
     
     case SC_STOP: {
     cout << "Stopping Service (" << task.entry.id << ")" << endl;
     
     if(tas_service_running(conf.services.at(pos).first) == false) {
     cout << "Service (" << task.entry.id << ") already stopped" << endl;
     } else {
     HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, conf.services.at(pos).first.pid);
     
     if(SUCCEEDED(hProc)) {
     if(TerminateProcess(hProc, 0) == TRUE) {
     conf.services.at(pos).first.pid = 0;
     conf.changes = true;
     }
     
     CloseHandle(hProc);
     } else {
     cout << "Unable to stop Process." << endl;
     }
     }
     
     
     if(task.action != SC_RESTART)
     break;
     }
     
     case SC_START: {
     cout << "Starting Service (" << task.entry.id << ")" << endl;
     
     PROCESS_INFORMATION pi = {0};
     STARTUPINFO si = {0};
     
     si.cb = sizeof(STARTUPINFO);
     
     string exec = string(conf.services.at(pos).first.path) + " " + string(conf.services.at(pos).first.param);
     
     if(CreateProcessA(0, (char*)exec.c_str(), 0, 0, FALSE, NORMAL_PRIORITY_CLASS, 0, 0, &si, &pi) == TRUE) {
     conf.services.at(pos).first.pid = pi.dwProcessId;
     cout << "Service running under PID " << conf.services.at(pos).first.pid << endl;
     conf.changes = true;
     } else {
     cout << "Could not start Service " << conf.services.at(pos).first.id << endl;
     return false;
     }
     } break;
     
     case SQ_STATUS: {
     if(conf.services.empty() == true){
     cout << "No Services in Database" << endl << endl;
     return false;
     }
     
     if(task.entry.id[0] == 0) {
     for(vector< pair<tas_entry, long> >::iterator ent=conf.services.begin(); ent != conf.services.end(); ent++, pos++) { 
     cout << "SERVICE (" << (*ent).first.id << "): " << endl
     << "\tPath: " << (*ent).first.path << endl
     << "\tParam: " << (*ent).first.param << endl
     << "\tState: " << (tas_service_running((tas_entry&)conf.services.at(pos))?"Running":"Stopped") << endl << endl;
     }
     } else {
     if(tas_service_exists(conf, task.entry.id, pos) == false) {
     cout << "Service (" << task.entry.id << ") doesn\'t exists. Skipping" << endl << endl;
     return false;
     }
     
     vector< pair<tas_entry, long> >::iterator ent = conf.services.begin()+pos; 
     
     cout << "SERVICE (" << (*ent).first.id << "): " << endl
     << "\tPath: " << (*ent).first.path << endl
     << "\tParam: " << (*ent).first.param << endl
     << "\tState: " << (tas_service_running((tas_entry&)conf.services.at(pos))?"Running":"Stopped") << endl << endl;
     }
     } break;
     }
     
     return true;
    }
    
    void
    tas_print_task(const tas_task& task, bool details) { 
     switch(task.type) {
     case SQ_ADD: {
     cout << "SQ_ADD" << endl;
     
     cout << "\tID: " << task.entry.id << endl;
     
     if(details == true) {
     cout << "\tPath: " << task.entry.path << endl;
     cout << "\tParam: " << task.entry.param << endl; 
     }
     } break;
     
     case SQ_REMOVE: {
     cout << "SQ_REMOVE" << endl;
     
     cout << "\tID: " << task.entry.id << endl;
     
     if(details == true) {
     cout << "\tPath: " << task.entry.path << endl;
     cout << "\tParam: " << task.entry.param << endl; 
     } 
     } break;
     
     case SQ_CONTROL: {
     cout << "SQ_CONTROL ";
     
     switch(task.action) {
     case SC_START: {
     cout << "(SC_START)" << endl;
     } break;
     
     case SC_STOP: {
     cout << "(SC_STOP)" << endl;
     } break;
     
     case SC_RESTART: {
     cout << "(SC_RESTART)" << endl;
     } break;
     }
     
     cout << "\tID: " << task.entry.id << endl;
     
     if(details == true) {
     cout << "\tPath: " << task.entry.path << endl;
     cout << "\tParam: " << task.entry.param << endl; 
     }
     } break;
     
     case SQ_STATUS: {
     cout << "SQ_STATUS " << endl;
     } break;
     }
     
     cout << endl;
    }
    
    bool
    tas_service_exists(const tas_config& conf, const char* id, size_t& pos) {
     pos = 0;
     
     for(vector< pair<tas_entry, long> >::const_iterator ent=conf.services.begin(); ent != conf.services.end(); ent++, pos++) {
     if(!strcmp(id, (*ent).first.id) && (*ent).second >= SDB_FIRST)
     return true;
     }
     
     return false;
    }
    
    bool
    tas_service_running(tas_entry& entry) {
     if(entry.pid == 0)
     return false;
     
     HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, entry.pid);
     
     if(FAILED(hProc)) {
     return false;
     }
     
     CloseHandle(hProc);
     return true;
    }
    Ein Beispiel:
    Code:
    tAppSvc -a ts3_sv1 C:\Teamspeak3\ts3server_win32.exe "Parameter1 Parameter2"
    tAppSvc -c ts3_sv1 start
    Wie immer würde ich gerne - aus Anstand - voher informiert werden, bevor ihr etwas kopiert.

    Grüße.
     
  2. 26. April 2011
    AW: ApplicationService

    Wie sollte ich das der guten alten sc.exe vorziehen? Ich meine das ist im Prinzip nicht anderes als eine rudimentäre Kopie der Windows Service Implementierung.
     
  3. 26. April 2011
    AW: ApplicationService

    sc funktioniert bei simplen Anwendungen nicht, deswegen wurde dieses Programm geschrieben.
     
  4. Video Script

    Videos zum Themenbereich

    * gefundene Videos auf YouTube, anhand der Überschrift.