entry.c

00001 /*
00002  * Copyright (c) 2005-2012 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  */
00008 
00009 #include "klone_conf.h"
00010 #include <stdlib.h>
00011 #include <unistd.h>
00012 #include <stdio.h>
00013 #include <u/libu.h>
00014 #include <klone/server.h>
00015 #include <klone/os.h>
00016 #include <klone/context.h>
00017 #include <klone/utils.h>
00018 #include <klone/version.h>
00019 #include "main.h"
00020 
00021 int facility = LOG_LOCAL0;
00022 
00023 static context_t c;
00024 context_t  *ctx = &c; /* exported */
00025 
00026 #ifdef OS_WIN
00027     /* Win32 service name and description */
00028     enum { SS_NAME_BUFSZ = 64, SS_DESC_BUFSZ = 256 };
00029     static char ss_name[SS_NAME_BUFSZ] = "kloned";
00030     static char ss_desc[SS_DESC_BUFSZ] = "kloned daemon";
00031 
00032     int InstallService(); 
00033     int RemoveService();
00034 #endif
00035 
00036 static void usage (void)
00037 {
00038     static const char *us = 
00039 "Usage: kloned OPTIONS ARGUMENTS                                            \n"
00040 "Version: %s "
00041 #ifdef SSL_OPENSSL
00042 "(with OpenSSL)"
00043 #endif
00044 #ifdef SSL_CYASSL
00045 "(with CyaSSL)"
00046 #endif
00047             " - Copyright (c) 2005-2012 KoanLogic s.r.l.                     \n"
00048 "All rights reserved.                                                       \n"
00049 "\n"
00050 "    -d          turn on debugging (forces iterative mode)                  \n"
00051 "    -f file     load an external config file                               \n"
00052 "    -c          override configuration lines via standard input            \n"
00053 "    -p file     save daemon PID to file                                    \n"
00054 "    -F          run in foreground                                          \n"
00055 "    -h          display this help                                          \n"
00056 #ifdef OS_WIN
00057 "    -i          install KLone Windows service                              \n"
00058 "    -u          remove KLone Windows service                               \n"
00059 #endif
00060 "    -V          print KLone version and exit                               \n"
00061 "    -n          do not chdir when daemonizing                              \n"
00062 "\n";
00063 
00064     fprintf(stderr, us, klone_version());
00065 
00066     exit(1);
00067 }
00068 
00069 static int parse_opt(int argc, char **argv)
00070 {
00071     int ret;
00072 #ifdef OS_WIN
00073         #define CMDLINE_FORMAT "nhVFdiuf:cp:"
00074 #else
00075         #define CMDLINE_FORMAT "nhVFdf:cp:"
00076 #endif
00077 
00078     /* set defaults */
00079     ctx->daemon++;
00080 
00081     while((ret = getopt(argc, argv, CMDLINE_FORMAT)) != -1)
00082     {
00083         switch(ret)
00084         {
00085         case 'f':   /* source a config file */
00086             ctx->ext_config = u_strdup(optarg);
00087             dbg_err_if(ctx->ext_config == NULL);
00088             u_dbg("ext config: %s", ctx->ext_config);
00089             break;
00090 
00091         case 'c':   /* override config from command-line */
00092             ctx->cmd_config = 1;
00093             break;
00094 
00095         case 'p':   /* PID file */
00096             ctx->pid_file = u_strdup(optarg);
00097             dbg_err_if(ctx->pid_file == NULL);
00098             u_dbg("PID file: %s", ctx->pid_file);
00099             break;
00100 
00101         case 'd':   /* turn on debugging */
00102             ctx->debug++;
00103             break;
00104 
00105         case 'F':   /* run in foreground (not as a daemon/service) */
00106             ctx->daemon = 0;
00107             break;
00108 
00109         case 'V':   /* print version and exit */
00110             u_print_version_and_exit();
00111             break;
00112 
00113         case 'n':   /* don't chdir in daemon mode */
00114             ctx->nochdir = 1;
00115             break;
00116 
00117 #ifdef OS_WIN
00118         case 'i':   /* install kloned service and exit */
00119             ctx->serv_op = SERV_INSTALL;
00120             break;
00121 
00122         case 'u':   /* uninstall kloned service and exit */
00123             ctx->serv_op = SERV_REMOVE;
00124             break;
00125 #endif
00126 
00127         default:
00128         case 'h': 
00129             usage();
00130         }
00131     }
00132 
00133     ctx->narg = argc - optind;
00134     ctx->arg = argv + optind;
00135 
00136     return 0;
00137 err:
00138     return ~0;
00139 }
00140 
00141 #if defined(OS_WIN)
00142 
00143 /* install the service with the service manager. after successful installation
00144    you can run the service from ControlPanel->AdminTools->Services */
00145 int InstallService(void) 
00146 {
00147     SC_HANDLE hSCM, hService;
00148     char szModulePathname[_MAX_PATH];
00149     SERVICE_DESCRIPTION sd = { ss_desc };
00150     int rc;
00151 
00152     // Open the SCM on this machine.
00153     hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
00154 
00155     dbg_err_if(hSCM == NULL);
00156 
00157     dbg_err_if(GetModuleFileName(GetModuleHandle(NULL), szModulePathname, 
00158         _MAX_PATH) == 0 );
00159 
00160     /* add this service to the SCM's database */
00161     hService = CreateService(hSCM, ss_name, ss_name,
00162         SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS, 
00163         SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
00164         szModulePathname, NULL, NULL, NULL, NULL, NULL);
00165 
00166     dbg_err_if(hService == NULL);
00167 
00168     rc = ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sd);
00169 
00170     dbg_err_if(rc == 0);
00171 
00172     /* success */
00173     MessageBox(NULL, "Service installation succeded", ss_name, MB_OK);
00174 
00175     return 0; 
00176 err:
00177     /* common error handling */
00178     warn_strerror(GetLastError());
00179     MessageBox(NULL, "Service installation error", ss_name, MB_OK);
00180     return ~0;
00181 }
00182 
00183 /* uninstall this service from the system */
00184 int RemoveService(void) 
00185 {
00186     SC_HANDLE hSCM, hService;
00187     int rc;
00188 
00189     hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
00190 
00191     dbg_err_if(hSCM == NULL);
00192 
00193     /* Open this service for DELETE access */
00194     hService = OpenService(hSCM, ss_name, DELETE);
00195 
00196     dbg_err_if(hService == NULL);
00197 
00198     /* Remove this service from the SCM's database */
00199     rc = DeleteService(hService);
00200 
00201     dbg_err_if(rc == 0);
00202 
00203     /* success */
00204     MessageBox(NULL, "Uninstall succeeded", ss_name, MB_OK);
00205     return 0;
00206 err:
00207     /* common error handling */
00208     warn_strerror(GetLastError());
00209     MessageBox(NULL, "Uninstall failed", ss_name, MB_OK);
00210     return ~0;
00211 }
00212 
00213 /* this function will be called by the SCM to request an action */
00214 DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, 
00215         LPVOID lpEventData, LPVOID lpContext)
00216 {
00217     enum { DENY_ACTION = 0xff };
00218 
00219     switch(dwControl)
00220     {
00221     case SERVICE_CONTROL_INTERROGATE:
00222         u_dbg("SERVICE_CONTROL_INTERROGATE" );
00223         SetServiceStatus(ctx->hServiceStatus, &ctx->status);
00224         return NO_ERROR;
00225 
00226     case SERVICE_CONTROL_STOP:
00227         u_dbg("SERVICE_CONTROL_STOP");
00228 
00229         if(ctx->status.dwCurrentState == SERVICE_STOPPED)
00230             return NO_ERROR; /* service already stopped */
00231 
00232         /* start the stop procedure, move to stop_pending state */
00233         ctx->status.dwCheckPoint = 1;
00234         ctx->status.dwWaitHint = 2000;
00235         ctx->status.dwCurrentState = SERVICE_STOP_PENDING; 
00236         SetServiceStatus(ctx->hServiceStatus, &ctx->status);
00237 
00238         server_stop(ctx->server);
00239         return NO_ERROR;
00240 
00241     case SERVICE_CONTROL_PAUSE:
00242         u_dbg("SERVICE_CONTROL_PAUSE");
00243         break;
00244 
00245     case SERVICE_CONTROL_CONTINUE:
00246         u_dbg("SERVICE_CONTROL_CONTINUE");
00247         break;
00248 
00249     case SERVICE_CONTROL_SHUTDOWN:
00250         u_dbg("SERVICE_CONTROL_SHUTDOWN");
00251         break;
00252 
00253     case SERVICE_CONTROL_PARAMCHANGE:
00254         u_dbg("SERVICE_CONTROL_PARAMCHANGE");
00255         break;
00256 
00257     default:
00258         u_dbg("SERVICE_CONTROL_UNKNOWN!!!!");
00259     }
00260     if(dwControl > 127 && dwControl < 255)
00261     {
00262         /* user defined control code */
00263         u_dbg("SERVICE_CONTROL_USER_DEFINED");
00264     }
00265 
00266     return ERROR_CALL_NOT_IMPLEMENTED;
00267 }
00268 
00269 /* this is the main function of the service. when this function returns the
00270  * service will be terminated by the SCM */
00271 void WINAPI ServiceMain(DWORD argc, PTSTR *argv)
00272 {
00273     SERVICE_STATUS *pSt = &ctx->status;
00274 
00275     /* register the service with the ServiceControlManager */
00276     ctx->hServiceStatus = RegisterServiceCtrlHandlerEx(ss_name, HandlerEx, ctx);
00277     dbg_err_if( ctx->hServiceStatus == 0 );
00278 
00279     /* init the status struct and update the service status */
00280     ZeroMemory(pSt, sizeof(SERVICE_STATUS));
00281     /* just one service in this exe */
00282     pSt->dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
00283     /* action supported by the service */
00284     pSt->dwControlsAccepted = SERVICE_ACCEPT_STOP;
00285     /* error returned while starting/stopping */
00286     pSt->dwWin32ExitCode = NO_ERROR;          
00287     /* service specific exit code */
00288     pSt->dwServiceSpecificExitCode = 0;          
00289     /* we're still initializing */
00290     pSt->dwCurrentState = SERVICE_START_PENDING;
00291     /* for progress operation */
00292     pSt->dwCheckPoint = 1;
00293     /* wait hint */
00294     pSt->dwWaitHint = 1000;
00295     /* set status */
00296     dbg_err_if(SetServiceStatus(ctx->hServiceStatus, pSt) == 0);
00297 
00298     dbg_err_if(parse_opt(argc, argv));
00299 
00300     /* load config and initialize */
00301     dbg_err_if(app_init());
00302 
00303     /* this should happen after initialization but I don't want to
00304        mess main.c with win32-only code */
00305 
00306     /* notify the end of initialization */
00307     u_dbg("SERVICE_RUNNING");
00308     ctx->status.dwCurrentState = SERVICE_RUNNING;
00309     ctx->status.dwCheckPoint = ctx->status.dwWaitHint = 0;    
00310     dbg_err_if(!SetServiceStatus(ctx->hServiceStatus, &ctx->status));
00311 
00312     /* run the main loop */
00313     app_run();
00314 
00315     /* let the service terminate */
00316     ctx->status.dwCurrentState = SERVICE_STOPPED;
00317     dbg_err_if(!SetServiceStatus(ctx->hServiceStatus, &ctx->status));
00318 
00319     return;
00320 
00321 err:
00322     warn_strerror(GetLastError());
00323 
00324     /* let the service terminate */
00325     ctx->status.dwCurrentState = SERVICE_STOPPED;
00326     dbg_err_if(!SetServiceStatus(ctx->hServiceStatus, &ctx->status));
00327 }
00328 
00329 int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, 
00330     LPSTR lpCmdLine, int nCmdShow)
00331 {
00332     SERVICE_TABLE_ENTRY ServiceTable[] = 
00333     {
00334         {   ss_name, ServiceMain }, 
00335         {   NULL, NULL }    /* end of list */
00336     };
00337     int rc = 0;
00338     const char *name, *desc;
00339 
00340     memset(ctx, 0, sizeof(context_t));
00341 
00342     /* parse command line parameters (and set ctx vars). NOTE: this work only 
00343        if launched by command line, for services see ServiceMain */
00344     dbg_err_if(parse_opt(__argc, __argv));
00345 
00346     if(ctx->serv_op)
00347     {
00348         /* load config and initialize */
00349         dbg_err_if(app_init());
00350 
00351         /* set up service name and description reading from the config file */
00352         name = u_config_get_subkey_value(ctx->config, "daemon.name");
00353         if(name)
00354             u_strlcpy(ss_name, name, sizeof ss_name);
00355 
00356         desc = u_config_get_subkey_value(ctx->config, "daemon.description");
00357         if(desc)
00358             u_strlcpy(ss_desc, desc, sizeof ss_desc);
00359 
00360         if(ctx->serv_op == SERV_INSTALL)
00361             dbg_err_if(InstallService());
00362         else    
00363             dbg_err_if(RemoveService());
00364     } else if(ctx->daemon) {
00365         u_dbg("Starting in service mode...");
00366         /* StartServiceCtrlDispatcher does not return until the service 
00367            has stopped running...  */
00368         if(!StartServiceCtrlDispatcher(ServiceTable))
00369             warn_strerror(GetLastError());
00370     } else {
00371         /* load config and initialize */
00372         dbg_err_if(app_init());
00373 
00374         rc = app_run();
00375     }
00376 
00377     dbg_err_if(app_term());
00378 
00379     /* if debugging then call exit(3) because it's needed to gprof to dump 
00380        its stats file (gmon.out) */
00381     if(ctx->debug)
00382         return rc;
00383 
00384     /* don't use return because exit(3) will be called and we don't want
00385        FILE* buffers to be automatically flushed (klog_file_t will write same 
00386        lines more times, once by the parent process and N times by any child
00387        created when FILE buffer was not empty) */
00388     _exit(rc); 
00389 err:
00390     app_term();
00391 
00392     if(ctx->debug) 
00393         return rc;
00394     _exit(EXIT_FAILURE);
00395 }
00396 
00397 #elif defined(OS_UNIX)
00398 
00399 int main(int argc, char **argv)
00400 {
00401     int rc = 0;
00402 
00403     memset(ctx, 0, sizeof(context_t));
00404 
00405     /* parse command line parameters (and set ctx vars) */
00406     dbg_err_if(parse_opt(argc, argv));
00407 
00408     if(getenv("GATEWAY_INTERFACE"))
00409         ctx->cgi = 1;
00410         
00411     /* load config and initialize */
00412     warn_err_ifm(app_init(), "kloned init error (more info in the log file)");
00413 
00414 #ifdef HAVE_FORK
00415     /* daemonize if not -F */
00416     if(ctx->daemon && !ctx->cgi)
00417         con_err_ifm(daemon(ctx->nochdir, 0), "daemon error");
00418 #endif  /* HAVE_FORK */
00419 
00420     /* save the PID in a file */
00421     if(ctx->pid_file)
00422         dbg_err_if(u_savepid(ctx->pid_file));
00423 
00424     /* jump to the main loop */
00425     rc = app_run();
00426 
00427     dbg_err_if(app_term());
00428 
00429     /* if debugging then call exit(3) because it's needed to gprof to dump 
00430        its stats file (gmon.out) */
00431     if(ctx->debug)
00432         return rc;
00433 
00434     /* don't use return because exit(3) will be called and we don't want
00435        FILE* buffers to be automatically flushed (klog_file_t will write same 
00436        lines more times, once by the parent process and N times by any child
00437        created when FILE buffer was not empty) */
00438     _exit(rc);
00439 err:
00440     app_term();
00441     if(ctx->debug)
00442         return ~0;
00443     _exit(EXIT_FAILURE);
00444 }
00445 
00446 #else
00447     #error unsupported platform
00448 #endif
00449 

←Products
Copyright © 2005-2012 - KoanLogic S.r.l. - All rights reserved