/* * Copyright 2008-2014 Arsen Chaloyan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: unimrcp_service.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $ */ #include #include #include #include #include "apt.h" #include "apt_pool.h" #define WIN_SERVICE_NAME "unimrcp" /** UniMRCP service register command enumeration */ typedef enum uni_service_register_e { USR_NONE, USR_REGISTER, USR_UNREGISTER } uni_service_register_e; /** UniMRCP service control command enumeration */ typedef enum uni_service_control_e { USC_NONE, USC_START, USC_STOP } uni_service_control_e; /** Display error message with Windows error code and description */ static void winerror(const char *msg) { char buf[128]; DWORD err = GetLastError(); int ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL); printf("%s: %lu %.*s\n", msg, err, ret, buf); } /** Register/install service in SCM */ static apt_bool_t uni_service_register(const char *root_dir_path, apr_pool_t *pool, const char *name, apt_bool_t autostart, unsigned long recover, int log_priority, const char *disp_name, const char *description) { apr_status_t status; char buf[4096]; static const size_t len = sizeof(buf); size_t pos = 0; char *root_dir; SERVICE_DESCRIPTION desc; SC_HANDLE sch_service; SC_HANDLE sch_manager; /* Normalize root directory path and make it absolute */ status = apr_filepath_merge(&root_dir, NULL, root_dir_path, APR_FILEPATH_NOTRELATIVE | APR_FILEPATH_NATIVE | APR_FILEPATH_TRUENAME, pool); if (status != APR_SUCCESS) { printf("Error making root directory absolute: %d %.512s\n", status, apr_strerror(status, buf, 512)); return FALSE; } buf[pos++] = '"'; pos = apr_cpystrn(buf + pos, root_dir, len - pos) - buf; if ((buf[pos - 1] != '\\') && (pos < len)) /* Add trailing backslash */ buf[pos++] = '\\'; pos = apr_cpystrn(buf + pos, "bin\\unimrcpserver.exe\" --service -o 2", len - pos) - buf; if (log_priority >= 0) { pos = apr_cpystrn(buf + pos, " -l ", len - pos) - buf; if (pos < len - 34) pos += strlen(itoa(log_priority, buf + pos, 10)); } if (name) { pos = apr_cpystrn(buf + pos, " --name \"", len - pos) - buf; pos = apr_cpystrn(buf + pos, name, len - pos) - buf; if ((buf[pos - 1] == '\\') && (pos < len)) /* `\"' might be misinterpreted as escape, so replace `\' with `\\' */ buf[pos++] = '\\'; if (pos < len) buf[pos++] = '"'; } pos = apr_cpystrn(buf + pos, " --root-dir \"", len - pos) - buf; pos = apr_cpystrn(buf + pos, root_dir, len - pos) - buf; if ((buf[pos - 1] == '\\') && (pos < len)) /* `\"' might be misinterpreted as escape, so replace `\' with `\\' */ buf[pos++] = '\\'; if (pos < len) buf[pos++] = '"'; if (pos < len) buf[pos] = 0; else { puts("Service Command Too Long"); return FALSE; } if (!disp_name || !*disp_name) { if (name) disp_name = apr_pstrcat(pool, name, " ", "UniMRCP Server", NULL); else disp_name = "UniMRCP Server"; } if (!description || !*description) description = "Launches UniMRCP Server"; sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); if(!sch_manager) { winerror("Failed to Open SCManager"); return FALSE; } sch_service = CreateService( sch_manager, name ? name : WIN_SERVICE_NAME, disp_name, GENERIC_EXECUTE | SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS, autostart ? SERVICE_AUTO_START : SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, buf,0,0,0,0,0); if(!sch_service) { winerror("Failed to Create Service"); CloseServiceHandle(sch_manager); return FALSE; } desc.lpDescription = (char *) description; if(!ChangeServiceConfig2(sch_service,SERVICE_CONFIG_DESCRIPTION,&desc)) { winerror("Failed to Set Service Description"); } if (recover) { SERVICE_FAILURE_ACTIONS sfa; SC_ACTION action; sfa.dwResetPeriod = 0; sfa.lpCommand = ""; sfa.lpRebootMsg = ""; sfa.cActions = 1; sfa.lpsaActions = &action; action.Delay = recover * 1000; action.Type = SC_ACTION_RESTART; if (!ChangeServiceConfig2(sch_service,SERVICE_CONFIG_FAILURE_ACTIONS,&sfa)) { winerror("Failed to Set Service Restart on Failure"); } } CloseServiceHandle(sch_service); CloseServiceHandle(sch_manager); printf("UniMRCP service %s registered\n", name ? name : WIN_SERVICE_NAME); return TRUE; } /** Unregister/uninstall service from SCM */ static apt_bool_t uni_service_unregister(const char *name) { apt_bool_t status = TRUE; SERVICE_STATUS ss_status; SC_HANDLE sch_service; SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); if (!name) name = WIN_SERVICE_NAME; if(!sch_manager) { winerror("Failed to Open SCManager"); return FALSE; } sch_service = OpenService(sch_manager,name,DELETE|SERVICE_STOP); if(!sch_service) { winerror("Failed to Open Service"); CloseServiceHandle(sch_manager); return FALSE; } ControlService(sch_service,SERVICE_CONTROL_STOP,&ss_status); if(!DeleteService(sch_service)) { winerror("Failed to Delete Service"); status = FALSE; } else printf("UniMRCP service %s unregistered\n", name); CloseServiceHandle(sch_service); CloseServiceHandle(sch_manager); return status; } /** Start service */ static apt_bool_t uni_service_start(const char *name) { apt_bool_t status = TRUE; SC_HANDLE sch_service; SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); if (!name) name = WIN_SERVICE_NAME; if(!sch_manager) { winerror("Failed to Open SCManager"); return FALSE; } sch_service = OpenService(sch_manager,name,SERVICE_START); if(!sch_service) { winerror("Failed to Open Service"); CloseServiceHandle(sch_manager); return FALSE; } if(!StartService(sch_service,0,NULL)) { winerror("Failed to Start Service"); status = FALSE; } else printf("UniMRCP service %s started\n", name); CloseServiceHandle(sch_service); CloseServiceHandle(sch_manager); return status; } /** Stop service */ static apt_bool_t uni_service_stop(const char *name) { apt_bool_t status = TRUE; SERVICE_STATUS ss_status; SC_HANDLE sch_service; SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); if (!name) name = WIN_SERVICE_NAME; if(!sch_manager) { winerror("Failed to Open SCManager"); return FALSE; } sch_service = OpenService(sch_manager,name,SERVICE_STOP); if(!sch_service) { winerror("Failed to Open Service"); CloseServiceHandle(sch_manager); return FALSE; } if(!ControlService(sch_service,SERVICE_CONTROL_STOP,&ss_status)) { winerror("Failed to Stop Service"); status = FALSE; } else printf("UniMRCP service %s stopped\n", name); CloseServiceHandle(sch_service); CloseServiceHandle(sch_manager); return status; } static void usage() { static apt_bool_t written = FALSE; if (written) return; printf( "\n" "Usage:\n" "\n" " unimrcpservice [options]\n" "\n" " Available options:\n" "\n" " -r [--register] rootdir : Register the Windows service.\n" "\n" " -u [--unregister] : Unregister the Windows service.\n" "\n" " -s [--start] : Start the Windows service.\n" "\n" " -t [--stop] : Stop the Windows service.\n" "\n" " -n [--name] svcname : Service name (default: unimrcp)\n" "\n" " -a [--autostart] : Start service after boot-up\n" "\n" " -f [--fail-restart] n : If crashed, restart after n secs\n" "\n" " -l [--log-prio] priority: Set the log priority.\n" " (0-emergency, ..., 7-debug)\n" " -p [--disp-name] title : Set service display name\n" " (default: [svcname] UniMRCP Server)\n" " -c [--description] desc : Set service description\n" " (default: Launches UniMRCP Server)\n" " -h [--help] : Show the help.\n" "\n"); written = TRUE; } int main(int argc, const char * const *argv) { apr_pool_t *pool; apr_status_t rv; apr_getopt_t *opt; apt_bool_t ret = TRUE; uni_service_register_e reg = USR_NONE; uni_service_control_e control = USC_NONE; const char *root_dir = ".."; const char *name = NULL; apt_bool_t autostart = FALSE; unsigned long recover = 0; int log_priority = -1; const char *disp_name = NULL; const char *description = NULL; static const apr_getopt_option_t opt_option[] = { /* long-option, short-option, has-arg flag, description */ { "register", 'r', TRUE, "register service" }, /* -r or --register arg */ { "unregister", 'u', FALSE, "unregister service" }, /* -u or --unregister */ { "start", 's', FALSE, "start service" }, /* -s or --start */ { "stop", 't', FALSE, "stop service" }, /* -t or --stop */ { "name", 'n', TRUE, "service name" }, /* -n or --name arg */ { "autostart", 'a', FALSE, "start automatically" },/* -a or --autostart */ { "fail-restart",'f', TRUE, "restart if fails" }, /* -f or --fail-restart arg */ { "log-prio", 'l', TRUE, "log priority" }, /* -l arg or --log-prio arg */ { "disp-name", 'p', TRUE, "display name" }, /* -p arg or --disp-name arg */ { "description", 'c', TRUE, "description" }, /* -c arg or --description arg */ { "help", 'h', FALSE, "show help" }, /* -h or --help */ { NULL, 0, 0, NULL }, /* end */ }; /* APR global initialization */ if(apr_initialize() != APR_SUCCESS) { apr_terminate(); return 1; } /* create APR pool */ pool = apt_pool_create(); if(!pool) { apr_terminate(); return 1; } rv = apr_getopt_init(&opt, pool , argc, argv); if(rv == APR_SUCCESS) { int optch; const char *optarg; while((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) { switch(optch) { case 'r': if ((reg == USR_NONE) || (reg == USR_REGISTER)) { reg = USR_REGISTER; root_dir = optarg; } else { puts("Incosistent arguments"); ret = FALSE; } break; case 'u': if ((reg == USR_NONE) || (reg == USR_UNREGISTER)) reg = USR_UNREGISTER; else { puts("Incosistent arguments"); ret = FALSE; } break; case 's': if ((control == USC_NONE) || (control == USC_START)) control = USC_START; else { puts("Incosistent arguments"); ret = FALSE; } break; case 't': if ((control == USC_NONE) || (control == USC_STOP)) control = USC_STOP; else { puts("Incosistent arguments"); ret = FALSE; } break; case 'n': name = optarg; break; case 'a': autostart = TRUE; break; case 'f': if (sscanf(optarg, "%lu", &recover) != 1) { puts("Invalid value for param --fail-restart"); ret = FALSE; } break; case 'l': if ((sscanf(optarg, "%d", &log_priority) != 1) || (log_priority < 0) || (log_priority > 7)) { puts("Invalid value for param --log-prio"); ret = FALSE; } break; case 'p': disp_name = optarg; break; case 'c': description = optarg; break; case 'h': usage(); break; } if (!ret) break; } if (ret && (((reg == USR_REGISTER) && (control == USC_STOP)) || ((reg == USR_UNREGISTER) && (control == USC_START)))) { ret = FALSE; puts("Inconsistent arguments"); } if((rv != APR_EOF) || !ret || (!reg && !control)) { ret = FALSE; usage(); } } while (ret) { /* No problem so far */ if (reg == USR_REGISTER) ret = uni_service_register(root_dir, pool, name, autostart, recover, log_priority, disp_name, description); if (!ret) break; if (control == USC_START) ret = uni_service_start(name); if (!ret) break; if (control == USC_STOP) ret = uni_service_stop(name); /* Do not break here, stop failure should not matter before unregistration */ if (reg == USR_UNREGISTER) ret = uni_service_unregister(name); break; } /* destroy APR pool */ apr_pool_destroy(pool); /* APR global termination */ apr_terminate(); return ret ? 0 : 1; }