2
0

unimrcp_service.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /*
  2. * Copyright 2008-2014 Arsen Chaloyan
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * $Id: unimrcp_service.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $
  17. */
  18. #include <windows.h>
  19. #include <apr_getopt.h>
  20. #include <apr_file_info.h>
  21. #include <apr_strings.h>
  22. #include "apt.h"
  23. #include "apt_pool.h"
  24. #define WIN_SERVICE_NAME "unimrcp"
  25. /** UniMRCP service register command enumeration */
  26. typedef enum uni_service_register_e {
  27. USR_NONE, USR_REGISTER, USR_UNREGISTER
  28. } uni_service_register_e;
  29. /** UniMRCP service control command enumeration */
  30. typedef enum uni_service_control_e {
  31. USC_NONE, USC_START, USC_STOP
  32. } uni_service_control_e;
  33. /** Display error message with Windows error code and description */
  34. static void winerror(const char *msg)
  35. {
  36. char buf[128];
  37. DWORD err = GetLastError();
  38. int ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  39. NULL,
  40. err,
  41. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  42. buf, sizeof(buf), NULL);
  43. printf("%s: %lu %.*s\n", msg, err, ret, buf);
  44. }
  45. /** Register/install service in SCM */
  46. static apt_bool_t uni_service_register(const char *root_dir_path, apr_pool_t *pool,
  47. const char *name,
  48. apt_bool_t autostart,
  49. unsigned long recover,
  50. int log_priority,
  51. const char *disp_name,
  52. const char *description)
  53. {
  54. apr_status_t status;
  55. char buf[4096];
  56. static const size_t len = sizeof(buf);
  57. size_t pos = 0;
  58. char *root_dir;
  59. SERVICE_DESCRIPTION desc;
  60. SC_HANDLE sch_service;
  61. SC_HANDLE sch_manager;
  62. /* Normalize root directory path and make it absolute */
  63. status = apr_filepath_merge(&root_dir, NULL, root_dir_path,
  64. APR_FILEPATH_NOTRELATIVE | APR_FILEPATH_NATIVE | APR_FILEPATH_TRUENAME, pool);
  65. if (status != APR_SUCCESS) {
  66. printf("Error making root directory absolute: %d %.512s\n", status,
  67. apr_strerror(status, buf, 512));
  68. return FALSE;
  69. }
  70. buf[pos++] = '"';
  71. pos = apr_cpystrn(buf + pos, root_dir, len - pos) - buf;
  72. if ((buf[pos - 1] != '\\') && (pos < len))
  73. /* Add trailing backslash */
  74. buf[pos++] = '\\';
  75. pos = apr_cpystrn(buf + pos, "bin\\unimrcpserver.exe\" --service -o 2", len - pos) - buf;
  76. if (log_priority >= 0) {
  77. pos = apr_cpystrn(buf + pos, " -l ", len - pos) - buf;
  78. if (pos < len - 34)
  79. pos += strlen(itoa(log_priority, buf + pos, 10));
  80. }
  81. if (name) {
  82. pos = apr_cpystrn(buf + pos, " --name \"", len - pos) - buf;
  83. pos = apr_cpystrn(buf + pos, name, len - pos) - buf;
  84. if ((buf[pos - 1] == '\\') && (pos < len))
  85. /* `\"' might be misinterpreted as escape, so replace `\' with `\\' */
  86. buf[pos++] = '\\';
  87. if (pos < len)
  88. buf[pos++] = '"';
  89. }
  90. pos = apr_cpystrn(buf + pos, " --root-dir \"", len - pos) - buf;
  91. pos = apr_cpystrn(buf + pos, root_dir, len - pos) - buf;
  92. if ((buf[pos - 1] == '\\') && (pos < len))
  93. /* `\"' might be misinterpreted as escape, so replace `\' with `\\' */
  94. buf[pos++] = '\\';
  95. if (pos < len)
  96. buf[pos++] = '"';
  97. if (pos < len)
  98. buf[pos] = 0;
  99. else {
  100. puts("Service Command Too Long");
  101. return FALSE;
  102. }
  103. if (!disp_name || !*disp_name) {
  104. if (name)
  105. disp_name = apr_pstrcat(pool, name, " ", "UniMRCP Server", NULL);
  106. else
  107. disp_name = "UniMRCP Server";
  108. }
  109. if (!description || !*description)
  110. description = "Launches UniMRCP Server";
  111. sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS);
  112. if(!sch_manager) {
  113. winerror("Failed to Open SCManager");
  114. return FALSE;
  115. }
  116. sch_service = CreateService(
  117. sch_manager,
  118. name ? name : WIN_SERVICE_NAME,
  119. disp_name,
  120. GENERIC_EXECUTE | SERVICE_CHANGE_CONFIG,
  121. SERVICE_WIN32_OWN_PROCESS,
  122. autostart ? SERVICE_AUTO_START : SERVICE_DEMAND_START,
  123. SERVICE_ERROR_NORMAL,
  124. buf,0,0,0,0,0);
  125. if(!sch_service) {
  126. winerror("Failed to Create Service");
  127. CloseServiceHandle(sch_manager);
  128. return FALSE;
  129. }
  130. desc.lpDescription = (char *) description;
  131. if(!ChangeServiceConfig2(sch_service,SERVICE_CONFIG_DESCRIPTION,&desc)) {
  132. winerror("Failed to Set Service Description");
  133. }
  134. if (recover) {
  135. SERVICE_FAILURE_ACTIONS sfa;
  136. SC_ACTION action;
  137. sfa.dwResetPeriod = 0;
  138. sfa.lpCommand = "";
  139. sfa.lpRebootMsg = "";
  140. sfa.cActions = 1;
  141. sfa.lpsaActions = &action;
  142. action.Delay = recover * 1000;
  143. action.Type = SC_ACTION_RESTART;
  144. if (!ChangeServiceConfig2(sch_service,SERVICE_CONFIG_FAILURE_ACTIONS,&sfa)) {
  145. winerror("Failed to Set Service Restart on Failure");
  146. }
  147. }
  148. CloseServiceHandle(sch_service);
  149. CloseServiceHandle(sch_manager);
  150. printf("UniMRCP service %s registered\n", name ? name : WIN_SERVICE_NAME);
  151. return TRUE;
  152. }
  153. /** Unregister/uninstall service from SCM */
  154. static apt_bool_t uni_service_unregister(const char *name)
  155. {
  156. apt_bool_t status = TRUE;
  157. SERVICE_STATUS ss_status;
  158. SC_HANDLE sch_service;
  159. SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS);
  160. if (!name) name = WIN_SERVICE_NAME;
  161. if(!sch_manager) {
  162. winerror("Failed to Open SCManager");
  163. return FALSE;
  164. }
  165. sch_service = OpenService(sch_manager,name,DELETE|SERVICE_STOP);
  166. if(!sch_service) {
  167. winerror("Failed to Open Service");
  168. CloseServiceHandle(sch_manager);
  169. return FALSE;
  170. }
  171. ControlService(sch_service,SERVICE_CONTROL_STOP,&ss_status);
  172. if(!DeleteService(sch_service)) {
  173. winerror("Failed to Delete Service");
  174. status = FALSE;
  175. } else
  176. printf("UniMRCP service %s unregistered\n", name);
  177. CloseServiceHandle(sch_service);
  178. CloseServiceHandle(sch_manager);
  179. return status;
  180. }
  181. /** Start service */
  182. static apt_bool_t uni_service_start(const char *name)
  183. {
  184. apt_bool_t status = TRUE;
  185. SC_HANDLE sch_service;
  186. SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS);
  187. if (!name) name = WIN_SERVICE_NAME;
  188. if(!sch_manager) {
  189. winerror("Failed to Open SCManager");
  190. return FALSE;
  191. }
  192. sch_service = OpenService(sch_manager,name,SERVICE_START);
  193. if(!sch_service) {
  194. winerror("Failed to Open Service");
  195. CloseServiceHandle(sch_manager);
  196. return FALSE;
  197. }
  198. if(!StartService(sch_service,0,NULL)) {
  199. winerror("Failed to Start Service");
  200. status = FALSE;
  201. } else
  202. printf("UniMRCP service %s started\n", name);
  203. CloseServiceHandle(sch_service);
  204. CloseServiceHandle(sch_manager);
  205. return status;
  206. }
  207. /** Stop service */
  208. static apt_bool_t uni_service_stop(const char *name)
  209. {
  210. apt_bool_t status = TRUE;
  211. SERVICE_STATUS ss_status;
  212. SC_HANDLE sch_service;
  213. SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS);
  214. if (!name) name = WIN_SERVICE_NAME;
  215. if(!sch_manager) {
  216. winerror("Failed to Open SCManager");
  217. return FALSE;
  218. }
  219. sch_service = OpenService(sch_manager,name,SERVICE_STOP);
  220. if(!sch_service) {
  221. winerror("Failed to Open Service");
  222. CloseServiceHandle(sch_manager);
  223. return FALSE;
  224. }
  225. if(!ControlService(sch_service,SERVICE_CONTROL_STOP,&ss_status)) {
  226. winerror("Failed to Stop Service");
  227. status = FALSE;
  228. } else
  229. printf("UniMRCP service %s stopped\n", name);
  230. CloseServiceHandle(sch_service);
  231. CloseServiceHandle(sch_manager);
  232. return status;
  233. }
  234. static void usage()
  235. {
  236. static apt_bool_t written = FALSE;
  237. if (written) return;
  238. printf(
  239. "\n"
  240. "Usage:\n"
  241. "\n"
  242. " unimrcpservice [options]\n"
  243. "\n"
  244. " Available options:\n"
  245. "\n"
  246. " -r [--register] rootdir : Register the Windows service.\n"
  247. "\n"
  248. " -u [--unregister] : Unregister the Windows service.\n"
  249. "\n"
  250. " -s [--start] : Start the Windows service.\n"
  251. "\n"
  252. " -t [--stop] : Stop the Windows service.\n"
  253. "\n"
  254. " -n [--name] svcname : Service name (default: unimrcp)\n"
  255. "\n"
  256. " -a [--autostart] : Start service after boot-up\n"
  257. "\n"
  258. " -f [--fail-restart] n : If crashed, restart after n secs\n"
  259. "\n"
  260. " -l [--log-prio] priority: Set the log priority.\n"
  261. " (0-emergency, ..., 7-debug)\n"
  262. " -p [--disp-name] title : Set service display name\n"
  263. " (default: [svcname] UniMRCP Server)\n"
  264. " -c [--description] desc : Set service description\n"
  265. " (default: Launches UniMRCP Server)\n"
  266. " -h [--help] : Show the help.\n"
  267. "\n");
  268. written = TRUE;
  269. }
  270. int main(int argc, const char * const *argv)
  271. {
  272. apr_pool_t *pool;
  273. apr_status_t rv;
  274. apr_getopt_t *opt;
  275. apt_bool_t ret = TRUE;
  276. uni_service_register_e reg = USR_NONE;
  277. uni_service_control_e control = USC_NONE;
  278. const char *root_dir = "..";
  279. const char *name = NULL;
  280. apt_bool_t autostart = FALSE;
  281. unsigned long recover = 0;
  282. int log_priority = -1;
  283. const char *disp_name = NULL;
  284. const char *description = NULL;
  285. static const apr_getopt_option_t opt_option[] = {
  286. /* long-option, short-option, has-arg flag, description */
  287. { "register", 'r', TRUE, "register service" }, /* -r or --register arg */
  288. { "unregister", 'u', FALSE, "unregister service" }, /* -u or --unregister */
  289. { "start", 's', FALSE, "start service" }, /* -s or --start */
  290. { "stop", 't', FALSE, "stop service" }, /* -t or --stop */
  291. { "name", 'n', TRUE, "service name" }, /* -n or --name arg */
  292. { "autostart", 'a', FALSE, "start automatically" },/* -a or --autostart */
  293. { "fail-restart",'f', TRUE, "restart if fails" }, /* -f or --fail-restart arg */
  294. { "log-prio", 'l', TRUE, "log priority" }, /* -l arg or --log-prio arg */
  295. { "disp-name", 'p', TRUE, "display name" }, /* -p arg or --disp-name arg */
  296. { "description", 'c', TRUE, "description" }, /* -c arg or --description arg */
  297. { "help", 'h', FALSE, "show help" }, /* -h or --help */
  298. { NULL, 0, 0, NULL }, /* end */
  299. };
  300. /* APR global initialization */
  301. if(apr_initialize() != APR_SUCCESS) {
  302. apr_terminate();
  303. return 1;
  304. }
  305. /* create APR pool */
  306. pool = apt_pool_create();
  307. if(!pool) {
  308. apr_terminate();
  309. return 1;
  310. }
  311. rv = apr_getopt_init(&opt, pool , argc, argv);
  312. if(rv == APR_SUCCESS) {
  313. int optch;
  314. const char *optarg;
  315. while((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) {
  316. switch(optch) {
  317. case 'r':
  318. if ((reg == USR_NONE) || (reg == USR_REGISTER)) {
  319. reg = USR_REGISTER;
  320. root_dir = optarg;
  321. } else {
  322. puts("Incosistent arguments");
  323. ret = FALSE;
  324. }
  325. break;
  326. case 'u':
  327. if ((reg == USR_NONE) || (reg == USR_UNREGISTER))
  328. reg = USR_UNREGISTER;
  329. else {
  330. puts("Incosistent arguments");
  331. ret = FALSE;
  332. }
  333. break;
  334. case 's':
  335. if ((control == USC_NONE) || (control == USC_START))
  336. control = USC_START;
  337. else {
  338. puts("Incosistent arguments");
  339. ret = FALSE;
  340. }
  341. break;
  342. case 't':
  343. if ((control == USC_NONE) || (control == USC_STOP))
  344. control = USC_STOP;
  345. else {
  346. puts("Incosistent arguments");
  347. ret = FALSE;
  348. }
  349. break;
  350. case 'n':
  351. name = optarg;
  352. break;
  353. case 'a':
  354. autostart = TRUE;
  355. break;
  356. case 'f':
  357. if (sscanf(optarg, "%lu", &recover) != 1) {
  358. puts("Invalid value for param --fail-restart");
  359. ret = FALSE;
  360. }
  361. break;
  362. case 'l':
  363. if ((sscanf(optarg, "%d", &log_priority) != 1) ||
  364. (log_priority < 0) || (log_priority > 7))
  365. {
  366. puts("Invalid value for param --log-prio");
  367. ret = FALSE;
  368. }
  369. break;
  370. case 'p':
  371. disp_name = optarg;
  372. break;
  373. case 'c':
  374. description = optarg;
  375. break;
  376. case 'h':
  377. usage();
  378. break;
  379. }
  380. if (!ret) break;
  381. }
  382. if (ret &&
  383. (((reg == USR_REGISTER) && (control == USC_STOP)) ||
  384. ((reg == USR_UNREGISTER) && (control == USC_START)))) {
  385. ret = FALSE;
  386. puts("Inconsistent arguments");
  387. }
  388. if((rv != APR_EOF) || !ret || (!reg && !control)) {
  389. ret = FALSE;
  390. usage();
  391. }
  392. }
  393. while (ret) { /* No problem so far */
  394. if (reg == USR_REGISTER)
  395. ret = uni_service_register(root_dir, pool, name, autostart, recover, log_priority, disp_name, description);
  396. if (!ret) break;
  397. if (control == USC_START)
  398. ret = uni_service_start(name);
  399. if (!ret) break;
  400. if (control == USC_STOP)
  401. ret = uni_service_stop(name);
  402. /* Do not break here, stop failure should not matter before unregistration */
  403. if (reg == USR_UNREGISTER)
  404. ret = uni_service_unregister(name);
  405. break;
  406. }
  407. /* destroy APR pool */
  408. apr_pool_destroy(pool);
  409. /* APR global termination */
  410. apr_terminate();
  411. return ret ? 0 : 1;
  412. }