/*************************************************************************** partimaged-main.cpp - main part of Partimaged ------------------- begin : Sun Jan 28 2001 copyright : (C) 2001 by Franck Ladurelle email : ladurelf@partimage.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_GETOPT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "net.h" #include "netserver.h" #include "messages.h" #include "image_disk.h" #include "common.h" #include "pathnames.h" #include "buffer.h" #include "partimaged-gui_newt.h" #include "partimaged-gui_dummy.h" #include "partimaged.h" #include "access.h" #include "privs.h" static char Revision[] = "CVS $Revision: 1.1.1.1 $"; CPrivs * g_privs=NULL; FILE * g_fDebug; FILE * g_fLocalDebug; extern DWORD g_dwDebugLevel; bool g_bBeDaemon; CNetServer * g_Server; CPartimagedInterface * g_Window; bool g_bMustLogin; bool g_bUseSSL; unsigned int g_uiNbClients; extern char * optarg; extern int optind; extern int opterr; #ifdef HAVE_GETOPT_H static struct option const long_options[] = { {"compilinfo", no_argument, NULL, 'i'}, {"port", required_argument, NULL, 'p'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"daemon", no_argument, NULL, 'D'}, {"dest", required_argument, NULL, 'd'}, {"chroot", required_argument, NULL, 'r'}, {"debug", required_argument, NULL, 'g'}, {"number", required_argument, NULL, 'N'}, {"nologin", no_argument, NULL, 'L'}, {"nossl", no_argument, NULL, 'n'}, {NULL, 0, NULL, 0} }; #endif //HAVE_GETOPT_H static char optstring[]="ip:hvDd:r:g:N:Ln"; bool g_bSigKill = false; bool g_bSigInt = false; sig_atomic_t child_exit_status; // ======================================================= static void catch_sigint(int signo) { if (signo == SIGTERM) { g_bSigKill = true; delete g_Server; delete g_Window; g_Server = NULL; g_Window = NULL; exit(0); } else if (signo == SIGINT) { g_bSigInt = true; delete g_Server; delete g_Window; g_Server = NULL; g_Window = NULL; exit(0); } else if (signo == SIGSEGV) { delete g_Server; delete g_Window; g_Server = NULL; g_Window = NULL; fprintf(stderr, i18n("Segmentation fault. Please report the bug and send the [%s] file to authors\n"), PARTIMAGED_LOG); exit(0); } showDebug(1, "Signal: %d\n", signo); } // ======================================================= static void catch_sigchild(int signal_number) { int status; int iClientPid; while( (iClientPid = waitpid (-1, &status, WNOHANG)) > 0) { showDebug(1, "Releasing client by pid : %d\n", iClientPid); g_Server->ReleaseClientByPid(iClientPid); // Save last child exit status in global variable child_exit_status = status; } } // ======================================================= void Usage() { printf ("=======================================================" "========================\n"); printf (i18n("Partition Image Daemon (http://www.partimage.org/) version %s\n" "---- distributed under the GPL 2 license (GNU General Public License) ----\n\n" "usage: partimaged [options]\n\n" "Options:\n" "* -h, --help show help\n" "* -v, --version show version\n" "* -d, --dest destination directory\n" "* -pX, --port=X server's port listening (defaults to %d)\n" "* -D, --daemon start in daemon mode, not foreground\n" "* -i, --compilinfo show compilation options used\n" "* -NX, --number=X maximum number of clients (default: 15)\n" "* -r dir, --chroot dir use chroot to improve security\n" "* -g, --debug=X set the debug level to X (default: 1)\n" "* -L, --nologin disable login from clients\n" "* -n, --nossl disable SSL\n"), PACKAGE_VERSION, SERVER_LISTEN_PORT); printf ("=======================================================" "========================\n"); exit(0); } // ======================================================= void Initialize() { char szTemp[2048]; // open log files as root if (g_dwDebugLevel) { g_fDebug = openFileDescriptorSecure(PARTIMAGED_LOG, "a", O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, S_IRUSR | S_IWUSR); if (!g_fDebug) { fprintf(stderr, i18n("Impossible to open logfile %s: %s\n"), PARTIMAGED_LOG, strerror(errno)); fprintf(stderr, i18n("Fix this and retry.\n")); } } else g_fDebug = NULL; if (!g_fDebug) { g_fDebug = fopen("/dev/null", "w"); if (!g_fDebug) exit(1); } g_fLocalDebug = fopen("/dev/null", "w"); if (!g_fLocalDebug) g_fDebug = stderr; // find PARTIMAGED_USER euid and register it to be used later. g_privs = new CPrivs(PARTIMAGED_USER); if (!g_privs) { fprintf(stderr, i18n("impossible to find user \"%s\"\n"),PARTIMAGED_USER); fprintf(stderr, i18n("add it and retry\n")); } if (!g_privs->AsSwitched()) showDebug(4, "Warning: we couldn't changed euid because we weren't " "launched as root\nexpect some access denied or troubles like this.\n"); // add usefull informations into logfile showDebug(1, "partimaged %s(%s)\n", PACKAGE_VERSION, Revision); showDebug(1,"-----------------------------------------\n"); formatCompilOptions(szTemp, sizeof(szTemp)); showDebug(1, "%s\n\n", szTemp); showDebug(1,"-----------------------------------------\n"); // we only exit here because we must write informations in logfile to help // debugging if (!g_privs) exit(0); } // ======================================================= int main(int argc, char *argv[]) { unsigned int client; int nOptCh, nRes, nServerPort = SERVER_LISTEN_PORT; char * szRootDir = NULL; char * szPeerName; char szTemp[2048]; // initialize language for i18n setlocale(LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); g_dwDebugLevel = DEFAULT_DEBUG_LEVEL; g_nDebugThreadMain = getpid(); g_bBeDaemon = false; g_bMustLogin = true; g_bUseSSL = true; g_uiNbClients = MAX_CLIENTS; #ifdef HAVE_GETOPT_H while ((nOptCh = getopt_long(argc, argv, optstring, long_options,NULL)) != EOF) #else while ((nOptCh = getopt(argc, argv, optstring)) != -1) #endif { switch (nOptCh) { case 'p': // listening port nServerPort = atoi(optarg); break; case 'N': // maximum number of clients g_uiNbClients = (unsigned int) atoi(optarg); break; case 'h': // help Usage(); case 'v': // version #ifdef HAVE_SSL printf(i18n("Partition Image Daemon version %s+SSL\n"), PACKAGE_VERSION); #else printf(i18n("Partition Image Daemon version %s\n"), PACKAGE_VERSION); #endif printf(i18n("(distributed under the GNU GPL 2)\n")); exit(0); case 'D': // daemonize g_bBeDaemon = true; break; case 'd': if (chdir(optarg) != 0) { printf(i18n("Directory %s: %s\n"), optarg, strerror(errno)); exit(-1); } break; case 'r': // change chroot if ((optarg) && (optarg[0])) szRootDir = strdup(optarg); break; case 'g': // debug level g_dwDebugLevel = atol(optarg); if (g_dwDebugLevel < 0 || g_dwDebugLevel > 10) g_dwDebugLevel = DEFAULT_DEBUG_LEVEL; break; case 'i': // compilation options formatCompilOptions(szTemp, sizeof(szTemp)); printf("%s\n", szTemp); return EXIT_SUCCESS; break; case 'L': // no login g_bMustLogin = false; break; case 'n': // no SSL g_bUseSSL = false; break; default: break; } } if (szRootDir) { // showDebug(2, "about to chroot to %s\n", szRootDir); // g_privs->Root(); if (chdir(szRootDir) < 0) { // g_privs->User(); fprintf(g_fDebug, i18n("failed to chdir to %s: %s\n"), szRootDir, strerror(errno)); delete g_Window; g_Window = NULL; exit(1); } if (chroot(szRootDir) < 0) { // g_privs->User(); fprintf(g_fDebug, i18n("failed to chroot: %s\n"), strerror(errno)); delete g_Window; g_Window = NULL; exit(1); } if (chdir("/") < 0) { // g_privs->User(); fprintf(g_fDebug, i18n("failed to chdir to /: %s\n"), strerror(errno)); delete g_Window; g_Window = NULL; exit(1); } // g_privs->User(); // showDebug(1, "root changed to %s\n", szRootDir); free(szRootDir); szRootDir = NULL; } // Warn: no showDebug must have been called before this point Initialize(); #ifdef MUST_LOGIN if (g_bMustLogin) { if (CheckAccessFile(PARTIMAGED_USERS)) exit(1); } #endif #ifdef HAVE_SSL if (g_bUseSSL) { if ( CheckAccessFile(KEYF) || CheckAccessFile(CERTF) ) { exit(1); } } #endif // pthread_t is no longer needed since we fork for each client // but kept for try catch exceptions to deal with g_uiNbClients pthread_t threads[g_uiNbClients] __attribute__ ((unused)) ; // register signals signal(SIGTERM, catch_sigint); signal(SIGINT, catch_sigint); signal(SIGSEGV, catch_sigint); // segmentation fault signal(SIGCHLD, catch_sigchild); // dont ignore child exits if (g_bBeDaemon) { // fork - so i'm not the owner of the process group any more int pgrp; int i = fork(); if (i < 0) { showDebug(1, "can't fork: %s\n", strerror(errno)); exit(1); } if (i > 0) exit(0); // no need for the parent any more pgrp = setsid(); if (pgrp < 0) { showDebug(1, "can't daemonize: %s\n", strerror(errno)); exit(1); } /* FIXME: this will break 'socket' call close(fileno(stdin)); close(fileno(stdout)); close(fileno(stderr)); */ #ifdef HAVE_SETPGID setpgid(0, getpid()); #else # ifdef SETPGRP_VOID setpgrp(); # else setpgrp(0, getpid()); # endif #endif } try { g_Server = new CNetServer(nServerPort); } catch ( CExceptions * excep ) { showDebug(1, "fatal error: get exception %d\n", excep->GetExcept()); fclose(g_fDebug); exit(1); } // g_Window = new CPartimagedInterfaceDummy(); g_Window = new CPartimagedInterfaceNewt(); g_Window->Status(i18n("Waiting for client ...")); while (1) { showDebug(1, "infernal loop\n"); try { client = g_Server->AcceptClient(); } catch ( CExceptions * excep ) { showDebug(1, "*** excep catched\n"); switch (excep -> GetExcept()) { case ERR_ERRNO: showDebug(1, "accept failed with %s\n", strerror(excep->get_dwArg1())); continue; // jump to next client case ERR_TOOMANY: showDebug(3, "too many clients -> one refused\n"); if (!g_bBeDaemon) g_Window->Status(i18n("too much client connected")); else fprintf(stderr, i18n("partimaged: too much clients connected\n")); continue; // jump to next client case ERR_REFUSED: showDebug(1, "refused: banner or version\n"); // client is undefined->we can't show it // g_Window->SetState(client, "error !"); // g_Window->SetLocation(client, "(wrong banner)"); continue; // jump to next client default: showDebug(1, "other exception: %d\n", excep->GetExcept()); continue; // jump to next client } showDebug(1, "ARG: reached unexpected point\n"); continue; // we shouldn't reach this point but never knows } showDebug(1, "client %d arrived\n", client); g_Window->SetState(client, i18n("connected")); szPeerName = g_Server -> GetPeer(client); g_Window->SetHostname(client, szPeerName); free(szPeerName); nRes = g_Server->ValidatePass(client); if (nRes) { g_Window->SetState(client, i18n("error!")); g_Window->SetLocation(client, i18n("(wrong password)")); } else { int rv = fork(); if ( rv < 0 ) { showDebug(1, "Cannot fork() on incoming connection - %s\n", strerror(errno)); continue; } if ( ! rv ) partimaged(&client); // child process else { // Store child process PID if (!g_Server->SetClientPid(client, rv)) { showDebug(1, "Cannot store client PID for client: %d\n", client); } } } } // infernal loop showDebug(1, "end of partimaged-main\n"); while(1); delete g_Server; g_Server = NULL; delete g_Window; g_Window = NULL; fclose(g_fDebug); }