Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

entry.c

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