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