KLone APIs | Modules | Data Structures | File List | Data Fields | Globals

entry.c

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