1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1998-2016. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20 #include <windows.h>
21 #include <winsvc.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include "erlsrv_global.h"
27 #include "erlsrv_registry.h"
28 #include "erlsrv_interactive.h"
29 #include "erlsrv_util.h" /* service_name */
30
31 #define DBG fwprintf(stderr,L"argv[0]:%s line %d\n",argv[0],__LINE__)
32 /* #define HARDDEBUG 1 */
33
34 #include <fcntl.h>
35
36 /* Really HAS to correcpond to the enum in erlsrv_registry.h */
37 static wchar_t *arg_tab[] = {
38 L"stopaction", L"st",
39 L"onfail", L"on",
40 L"machine", L"m",
41 L"env", L"e",
42 L"workdir", L"w",
43 L"priority", L"p",
44 L"sname", L"sn",
45 L"name", L"n",
46 L"args", L"ar",
47 L"debugtype", L"d",
48 L"internalservicename",L"i",
49 L"comment",L"c",
50 NULL, NULL
51 };
52
generate_real_service_name(wchar_t * display_name)53 static wchar_t *generate_real_service_name(wchar_t *display_name){
54 SYSTEMTIME systime;
55 FILETIME ftime;
56 int len=(wcslen(display_name)+(8*2)+1);
57 wchar_t *buff;
58 wchar_t *tmp = _wcsdup(display_name);
59 int i;
60 buff = (wchar_t*) malloc(len*sizeof(wchar_t));
61
62 /* 2 Hex chars for each byte in a DWORD */
63 GetSystemTime(&systime);
64 SystemTimeToFileTime(&systime,&ftime);
65 /* Remove trailing version info to avoid user confusion */
66 for(i = (wcslen(tmp)-1);i > 0; --i)
67 if(tmp[i] == L'_'){
68 tmp[i] = L'\0';
69 break;
70 }
71 swprintf(buff,len,L"%s%08x%08x",tmp,
72 ftime.dwHighDateTime,
73 ftime.dwLowDateTime);
74 free(tmp);
75 return buff;
76 }
77
lookup_arg(wchar_t * arg)78 static int lookup_arg(wchar_t *arg){
79 int i;
80 if(*arg != L'-' && *arg != L'/')
81 return -1;
82 for(i=0; arg_tab[i] != NULL; i += 2){
83 if(!_wcsnicmp(arg_tab[i],arg+1,wcslen(arg+1)) &&
84 !_wcsnicmp(arg_tab[i+1],arg+1,wcslen(arg_tab[i+1])))
85 return (i / 2);
86 }
87 return -1;
88 }
89
90
91
edit_env(wchar_t * edit,wchar_t * oldenv)92 wchar_t *edit_env(wchar_t *edit, wchar_t *oldenv){
93 wchar_t **arg;
94 wchar_t *value;
95 wchar_t *name = wcsdup(edit);
96 int i;
97 wchar_t *tmp;
98 arg = env_to_arg(oldenv);
99 value = wcschr(name,L'=');
100 if(value){
101 *(value++) = L'\0';
102 if(*value == L'\0')
103 value = NULL;
104 }
105 for(i=0;arg[i] != NULL; ++i){
106 tmp = wcschr(arg[i],L'=');
107 if(((int) wcslen(name)) == (tmp - arg[i]) &&
108 !_wcsnicmp(name,arg[i], tmp - arg[i]))
109 break;
110 }
111 if(arg[i] != NULL){
112 free(arg[i]);
113 if(value){
114 arg[i] = wcsdup(edit);
115 } else {
116 do {
117 arg[i] = arg[i+1];
118 ++i;
119 } while(arg[i] != NULL);
120 }
121 } else if(value){ /* add to arg, which is always allocated
122 to hold one extra environment variable*/
123 arg[i] = wcsdup(edit);
124 arg[i+1] = NULL;
125 }
126 free(name);
127 return arg_to_env(arg);
128 }
129
130 int last_error = 0;
131
print_last_error(void)132 void print_last_error(void){
133 char *mes;
134 FormatMessage(
135 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
136 NULL,
137 (last_error) ? last_error : GetLastError(),
138 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
139 (LPTSTR) &mes,
140 0,
141 NULL );
142 fwprintf(stderr,L"Error: %S",mes);
143 LocalFree(mes);
144 }
145
get_last_error(void)146 static int get_last_error(void)
147 {
148 return (last_error) ? last_error : GetLastError();
149 }
150
install_service(void)151 static BOOL install_service(void){
152 SC_HANDLE scm;
153 SC_HANDLE service;
154 wchar_t filename[MAX_PATH + 3];
155 DWORD fnsiz=MAX_PATH;
156 wchar_t dependant[] = { L'L',L'a',L'n',L'm',L'a',L'n',
157 L'W',L'o',L'r',L'k',L's',L't',
158 L'a',L't',L'i',L'o',L'n',L'\0',L'\0'};
159
160 if(!(fnsiz = GetModuleFileNameW(NULL, filename, fnsiz)))
161 return FALSE;
162 if(wcschr(filename,L' ')){
163 memmove(filename+1,filename,fnsiz*sizeof(wchar_t));
164 filename[0] = L'\"'; /* " */
165 filename[fnsiz+1] = L'\"'; /* " */
166 filename[fnsiz+2] = L'\0';
167 }
168 if((scm = OpenSCManager(NULL,
169 NULL,
170 SC_MANAGER_CONNECT |
171 SC_MANAGER_CREATE_SERVICE))
172 == NULL){
173 last_error = GetLastError();
174 return FALSE;
175 }
176 service = CreateServiceW(scm,
177 real_service_name,
178 service_name,
179 SERVICE_ALL_ACCESS &
180 ~(SERVICE_PAUSE_CONTINUE),
181 SERVICE_WIN32_OWN_PROCESS,
182 SERVICE_AUTO_START,
183 SERVICE_ERROR_NORMAL,
184 filename,
185 NULL,
186 NULL,
187 dependant,
188 NULL,
189 NULL);
190 if(service == NULL){
191 CloseServiceHandle(scm);
192 last_error = GetLastError();
193 return FALSE;
194 }
195 CloseServiceHandle(service);
196 CloseServiceHandle(scm);
197 return TRUE;
198 }
199
remove_service(void)200 static BOOL remove_service(void){
201 SC_HANDLE scm;
202 SC_HANDLE service;
203 if((scm = OpenSCManager(NULL,
204 NULL,
205 GENERIC_WRITE))
206 == NULL)
207 return FALSE;
208 service = OpenServiceW(scm,
209 real_service_name,
210 SERVICE_ALL_ACCESS);
211 if(service == NULL){
212 CloseServiceHandle(scm);
213 return FALSE;
214 }
215 if(!DeleteService(service)){
216 last_error = GetLastError();
217 return FALSE;
218 }
219 CloseServiceHandle(service);
220 CloseServiceHandle(scm);
221 return TRUE;
222 }
223
open_service_control(SC_HANDLE * scm,SC_HANDLE * service)224 static BOOL open_service_control(SC_HANDLE *scm, SC_HANDLE *service){
225 if((*scm = OpenSCManager(NULL,
226 NULL,
227 SC_MANAGER_ALL_ACCESS))
228 == NULL)
229 return FALSE;
230 *service = OpenServiceW(*scm,
231 real_service_name,
232 SERVICE_ALL_ACCESS);
233 if(service == NULL){
234 CloseServiceHandle(*scm);
235 return FALSE;
236 }
237 return TRUE;
238 }
239
open_service_config(SC_HANDLE * scm,SC_HANDLE * service)240 static BOOL open_service_config(SC_HANDLE *scm, SC_HANDLE *service){
241 if((*scm = OpenSCManager(NULL,
242 NULL,
243 /*GENERIC_WRITE | GENERIC_EXECUTE*/
244 SC_MANAGER_ALL_ACCESS))
245 == NULL){
246 last_error = GetLastError();
247 return FALSE;
248 }
249 *service = OpenServiceW(*scm,
250 real_service_name,
251 /*GENERIC_WRITE*/
252 SERVICE_ALL_ACCESS);
253 if(service == NULL){
254 last_error = GetLastError();
255 CloseServiceHandle(*scm);
256 return FALSE;
257 }
258 return TRUE;
259 }
260
set_service_comment(wchar_t * comment)261 static BOOL set_service_comment(wchar_t *comment) {
262 SC_HANDLE scm;
263 SC_HANDLE service;
264 SERVICE_DESCRIPTIONW sd;
265 BOOL ret = TRUE;
266 sd.lpDescription = comment;
267 if (!open_service_config(&scm,&service)) {
268 return FALSE;
269 }
270 if (!ChangeServiceConfig2W(service,SERVICE_CONFIG_DESCRIPTION,&sd)) {
271 last_error = GetLastError();
272 ret = FALSE;
273 }
274 CloseServiceHandle(service);
275 CloseServiceHandle(scm);
276 return ret;
277 }
278
wait_service_trans(DWORD initial,DWORD passes,DWORD goal,int timeout)279 static BOOL wait_service_trans(DWORD initial, DWORD passes, DWORD goal,
280 int timeout)
281 {
282 SC_HANDLE scm;
283 SC_HANDLE service;
284 int moved = 0;
285 BOOL ret;
286 int i;
287 SERVICE_STATUS stat;
288
289 if(! open_service_config(&scm,&service))
290 return FALSE;
291 for(i = 0; i < timeout; ++i){
292 if(!QueryServiceStatus(service,&stat)){
293 last_error = GetLastError();
294 ret = FALSE;
295 goto out;
296 }
297 if(stat.dwCurrentState == initial){
298 if(moved){
299 ret = FALSE;
300
301 /*
302 * The exitcode is usually strange when we tried to stop and failed,
303 * to report a timeout is more appropriate.
304 */
305 if(goal == SERVICE_STOPPED)
306 last_error = ERROR_SERVICE_REQUEST_TIMEOUT;
307 else
308 last_error = stat.dwWin32ExitCode;
309 goto out;
310 }
311 } else if(stat.dwCurrentState == passes){
312 moved = 1;
313 } else if(stat.dwCurrentState == goal){
314 ret = TRUE;
315 goto out;
316 }
317 Sleep(1000);
318 }
319 ret = FALSE;
320 last_error = ERROR_SERVICE_REQUEST_TIMEOUT;
321 out:
322 CloseServiceHandle(scm);
323 CloseServiceHandle(service);
324 return ret;
325 }
326
stop_service(void)327 static BOOL stop_service(void){
328 SC_HANDLE scm;
329 SC_HANDLE service;
330 BOOL ret;
331 SERVICE_STATUS ss;
332
333 if(!open_service_control(&scm,&service)){
334 #ifdef HARDDEBUG
335 fwprintf(stderr,L"Failed to open service.\n");
336 #endif
337 return FALSE;
338 }
339 ret = ControlService(service,SERVICE_CONTROL_STOP,&ss);
340 if(!ret){
341 last_error = GetLastError();
342 }
343 CloseServiceHandle(service);
344 CloseServiceHandle(scm);
345 #ifdef HARDDEBUG
346 if(!ret)
347 {
348 fwprintf(stderr,L"Failed to control service.\n");
349 print_last_error();
350 }
351 #endif
352 return ret;
353 }
354
start_service(void)355 static BOOL start_service(void){
356 SC_HANDLE scm;
357 SC_HANDLE service;
358 BOOL ret;
359
360 if(!open_service_control(&scm,&service))
361 return FALSE;
362
363 ret = StartService(service,0,NULL);
364 if(!ret){
365 last_error = GetLastError();
366 }
367 CloseServiceHandle(service);
368 CloseServiceHandle(scm);
369 return ret;
370 }
371
disable_service(void)372 static BOOL disable_service(void){
373 SC_HANDLE scm;
374 SC_HANDLE service;
375 BOOL ret;
376
377 if(!open_service_config(&scm,&service))
378 return FALSE;
379
380 ret = ChangeServiceConfig(service,
381 SERVICE_NO_CHANGE,
382 SERVICE_DISABLED,
383 SERVICE_NO_CHANGE,
384 NULL,
385 NULL,
386 NULL,
387 NULL,
388 NULL,
389 NULL,
390 NULL);
391 if(!ret){
392 last_error = GetLastError();
393 }
394
395 CloseServiceHandle(service);
396 CloseServiceHandle(scm);
397 return ret;
398 }
399
enable_service(void)400 static BOOL enable_service(void){
401 SC_HANDLE scm;
402 SC_HANDLE service;
403 BOOL ret;
404
405 if(!open_service_config(&scm,&service))
406 return FALSE;
407
408 ret = ChangeServiceConfig(service,
409 SERVICE_NO_CHANGE,
410 SERVICE_AUTO_START,
411 SERVICE_NO_CHANGE,
412 NULL,
413 NULL,
414 NULL,
415 NULL,
416 NULL,
417 NULL,
418 NULL);
419
420 if(!ret){
421 last_error = GetLastError();
422 }
423 CloseServiceHandle(service);
424 CloseServiceHandle(scm);
425 return ret;
426 }
427
set_interactive(BOOL interactive)428 static BOOL set_interactive(BOOL interactive){
429 SC_HANDLE scm;
430 SC_HANDLE service;
431 BOOL ret;
432
433 if(!open_service_config(&scm,&service))
434 return FALSE;
435
436 ret = ChangeServiceConfig(service,
437 SERVICE_WIN32_OWN_PROCESS | ((interactive) ?
438 SERVICE_INTERACTIVE_PROCESS : 0),
439 SERVICE_NO_CHANGE,
440 SERVICE_NO_CHANGE,
441 NULL,
442 NULL,
443 NULL,
444 NULL,
445 NULL,
446 NULL,
447 NULL);
448
449 if(!ret){
450 last_error = GetLastError();
451 }
452 CloseServiceHandle(service);
453 CloseServiceHandle(scm);
454 return ret;
455 }
456
457
458 RegEntry *old_entries = NULL;
459
fetch_current(RegEntry * new)460 BOOL fetch_current(RegEntry *new){
461 int i;
462
463 if(!(old_entries = get_keys(service_name)))
464 return FALSE;
465 for(i=0;i<num_reg_entries;++i)
466 new[i] = old_entries[i];
467 return TRUE;
468 }
469
cleanup_old()470 void cleanup_old(){
471 if(old_entries != NULL)
472 free_keys(old_entries);
473 }
474
fill_in_defaults(RegEntry * new)475 BOOL fill_in_defaults(RegEntry *new){
476 wchar_t filename[MAX_PATH];
477 wchar_t *ptr;
478
479
480 if(!GetModuleFileNameW(NULL, filename, MAX_PATH))
481 return FALSE;
482 for(ptr = filename + wcslen(filename) - 1;
483 ptr > filename && *ptr != L'\\';
484 --ptr)
485 ;
486 if(*ptr == L'\\')
487 ++ptr;
488 *ptr = L'\0';
489
490 ptr = (wchar_t*) malloc((wcslen(filename)+wcslen(ERLANG_MACHINE)+1)*sizeof(wchar_t));
491 wcscpy(ptr,filename);
492 wcscat(ptr,ERLANG_MACHINE);
493
494 new[StopAction].data.string = L"";
495 new[OnFail].data.value = ON_FAIL_IGNORE;
496 new[Machine].data.string = ptr;
497 new[Machine].data.expand.unexpanded = ptr;
498 new[Env].data.string = L"\0";
499 new[WorkDir].data.string = new[WorkDir].data.expand.unexpanded = L"";
500 new[Priority].data.value = NORMAL_PRIORITY_CLASS;
501 new[SName].data.string = service_name;
502 new[Name].data.string = L"";
503 new[Args].data.string = new[Args].data.expand.unexpanded = L"";
504 new[DebugType].data.value = DEBUG_TYPE_NO_DEBUG;
505 new[InternalServiceName].data.string = real_service_name;
506 new[Comment].data.string = L"";
507 return TRUE;
508 }
509
do_usage(wchar_t * arg0)510 int do_usage(wchar_t *arg0){
511 wprintf(L"Usage:\n");
512 wprintf(L"%s {set | add} <servicename>\n"
513 L"\t[-st[opaction] [<erlang shell command>]]\n"
514 L"\t[-on[fail] [{reboot | restart | restart_always}]]\n"
515 L"\t[-m[achine] [<erl-command>]]\n"
516 L"\t[-e[nv] [<variable>[=<value>]]]\n"
517 L"\t[-w[orkdir] [<directory>]]\n"
518 L"\t[-p[riority] [{low|high|realtime}]]\n"
519 L"\t[{-sn[ame] | -n[ame]} [<nodename>]]\n"
520 L"\t[-d[ebugtype] [{new|reuse|console}]]\n"
521 L"\t[-ar[gs] [<limited erl arguments>]]\n\n"
522 L"%s {start | start_disabled | stop | disable | enable} <servicename>\n\n"
523 L"%s remove <servicename>\n\n"
524 L"%s rename <servicename> <servicename>\n\n"
525 L"%s list [<servicename>]\n\n"
526 L"%s help\n\n",
527 arg0,arg0,arg0,arg0,arg0,arg0);
528 wprintf(L"Manipulates Erlang system services on Windows NT.\n\n");
529 wprintf(L"When no parameter to an option is specified, the option\n"
530 L"is reset to it's default value. To set an empty argument\n"
531 L"list, give option -args as last option on command line "
532 L"with\n"
533 L"no arguments.\n\n");
534 wprintf(L"See Erlang documentation for full description.\n");
535 return 0;
536 }
537
do_manage(int argc,wchar_t ** argv)538 int do_manage(int argc, wchar_t **argv){
539 wchar_t *action = argv[1];
540 RegEntry *current = empty_reg_tab();
541
542 if(argc < 3){
543 fwprintf(stderr,L"%s: No servicename given!\n",argv[0]);
544 do_usage(argv[0]);
545 return 1;
546 }
547 service_name = argv[2];
548 if(!fetch_current(current)){
549 fwprintf(stderr,L"%s: The service %s is not an erlsrv controlled service.\n",
550 argv[0],service_name);
551 return 1;
552 }
553 real_service_name = _wcsdup(current[InternalServiceName].data.string);
554 free_keys(current);
555
556 if(!_wcsicmp(action,L"start")){
557 if(!start_service()){
558 fwprintf(stderr,L"%s: Failed to start service %s.\n",
559 argv[0],service_name);
560 print_last_error();
561 return 1;
562 } else {
563 if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING,
564 SERVICE_RUNNING, 60)){
565 fwprintf(stderr,L"%s: Failed to start service %s.\n",
566 argv[0],service_name);
567 print_last_error();
568 return 1;
569 }
570 wprintf(L"%s: Service %s started.\n",
571 argv[0],service_name);
572 return 0;
573 }
574 }
575 if(!_wcsicmp(action,L"start_disabled")){
576 if(!enable_service()){
577 fwprintf(stderr,L"%s: Failed to enable service %s.\n",
578 argv[0],service_name);
579 print_last_error();
580 return 1;
581 }
582 if(!start_service() && get_last_error() != ERROR_SERVICE_ALREADY_RUNNING){
583 fwprintf(stderr,L"%s: Failed to start service %s.\n",
584 argv[0],service_name);
585 print_last_error();
586 goto failure_starting;
587 }
588
589 if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING,
590 SERVICE_RUNNING, 60)){
591 fwprintf(stderr,L"%s: Failed to start service %s.\n",
592 argv[0],service_name);
593 print_last_error();
594 goto failure_starting;
595 }
596
597 if(!disable_service()){
598 fwprintf(stderr,L"%s: Failed to disable service %s.\n",
599 argv[0],service_name);
600 print_last_error();
601 return 1;
602 }
603 wprintf(L"%s: Service %s started.\n",
604 argv[0],service_name);
605 return 0;
606 failure_starting:
607 if(!disable_service()){
608 fwprintf(stderr,L"%s: Failed to disable service %s.\n",
609 argv[0],service_name);
610 print_last_error();
611 }
612 return 1;
613 }
614 if(!_wcsicmp(action,L"stop")){
615 if(!stop_service()){
616 fwprintf(stderr,L"%s: Failed to stop service %s.\n",
617 argv[0],service_name);
618 print_last_error();
619 return 1;
620 } else {
621 if(!wait_service_trans(SERVICE_RUNNING, SERVICE_STOP_PENDING,
622 SERVICE_STOPPED, 60)){
623 fwprintf(stderr,L"%s: Failed to stop service %s.\n",
624 argv[0],service_name);
625 print_last_error();
626 return 1;
627 }
628 wprintf(L"%s: Service %s stopped.\n",
629 argv[0],service_name);
630 return 0;
631 }
632 }
633 if(!_wcsicmp(action,L"disable")){
634 #if 0
635 if(stop_service()){
636 wprintf(L"%s: Service %s stopped.\n",
637 argv[0],service_name);
638 }
639 #endif
640 if(!disable_service()){
641 fwprintf(stderr,L"%s: Failed to disable service %s.\n",
642 argv[0],service_name);
643 print_last_error();
644 return 1;
645 } else {
646 wprintf(L"%s: Service %s disabled.\n",
647 argv[0],service_name);
648 return 0;
649 }
650 }
651 if(!_wcsicmp(action,L"enable")){
652 if(!enable_service()){
653 fwprintf(stderr,L"%s: Failed to enable service %s.\n",
654 argv[0],service_name);
655 print_last_error();
656 return 1;
657 } else {
658 wprintf(L"%s: Service %s enabled.\n",
659 argv[0],service_name);
660 return 0;
661 }
662 }
663 fwprintf(stderr,L"%s: Unrecignized argument %s.\n",
664 argv[0],action);
665 return 1;
666 }
667
do_add_or_set(int argc,wchar_t ** argv)668 int do_add_or_set(int argc, wchar_t **argv){
669 RegEntry *new_entries;
670 RegEntry *default_entries;
671 int add = 0;
672 int i;
673 int current;
674 int set_comment = 0;
675 new_entries = empty_reg_tab();
676 default_entries = empty_reg_tab();
677 if(argc < 3){
678 fwprintf(stderr,L"%s: No servicename given!\n",argv[0]);
679 do_usage(argv[0]);
680 return 1;
681 }
682 service_name = argv[2];
683 if(!_wcsicmp(argv[1],L"add")){
684 if(fetch_current(default_entries)){
685 fwprintf(stderr,L"%s: A service with the name %s already "
686 L"exists.\n",
687 argv[0],service_name);
688 return 1;
689 }
690 real_service_name = generate_real_service_name(service_name);
691 if(!fill_in_defaults(new_entries)){
692 fwprintf(stderr,L"%s: Internal error.\n", argv[0]);
693 return 1;
694 }
695 add = 1;
696 } else {
697 if(!fetch_current(new_entries)){
698 fwprintf(stderr,L"%s: No service with the name %s exists.\n",
699 argv[0], service_name);
700 return 1;
701 }
702 real_service_name = new_entries[InternalServiceName].data.string;
703 }
704
705 if(!fill_in_defaults(default_entries)){
706 fwprintf(stderr,L"%s: Internal error.\n", argv[0]);
707 return 1;
708 }
709
710 /* make sure env is malloced... */
711 new_entries[Env].data.string = envdup(new_entries[Env].data.string);
712
713 for(i = 3; i < argc; ++i){
714 switch((current = lookup_arg(argv[i]))){
715 case Comment:
716 set_comment = 1;
717 case Machine:
718 case WorkDir:
719 case Args:
720 if(i+1 >= argc){
721 new_entries[current].data.string =
722 default_entries[current].data.string;
723 new_entries[current].data.expand.unexpanded =
724 default_entries[current].data.expand.unexpanded;
725 } else {
726 new_entries[current].data.expand.unexpanded =
727 new_entries[current].data.string = argv[i+1];
728 ++i;
729 }
730 break;
731 case SName:
732 new_entries[Name].data.string = L"";
733 case StopAction:
734 case Name:
735 if(i+1 >= argc ||
736 *argv[i+1] == L'-' || *argv[i+1] == L'/'){
737 new_entries[current].data.string =
738 default_entries[current].data.string;
739 } else {
740 new_entries[current].data.string = argv[i+1];
741 ++i;
742 }
743 break;
744 case OnFail:
745 if(i+1 >= argc ||
746 *argv[i+1] == L'-' || *argv[i+1] == L'/'){
747 new_entries[current].data.value =
748 default_entries[current].data.value;
749 } else {
750 if(!_wcsicmp(argv[i+1],L"reboot"))
751 new_entries[current].data.value = ON_FAIL_REBOOT;
752 else if(!_wcsicmp(argv[i+1],L"restart"))
753 new_entries[current].data.value = ON_FAIL_RESTART;
754 else if(!_wcsicmp(argv[i+1],L"restart_always"))
755 new_entries[current].data.value = ON_FAIL_RESTART_ALWAYS;
756 else {
757 fwprintf(stderr,L"%s: Unrecognized keyword value %s.\n",
758 argv[0],argv[i+1]);
759 return 1;
760 }
761 ++i;
762 }
763 break;
764 case DebugType:
765 if(i+1 >= argc ||
766 *argv[i+1] == L'-' || *argv[i+1] == L'/'){
767 new_entries[current].data.value =
768 default_entries[current].data.value;
769 } else {
770 if(!_wcsicmp(argv[i+1],L"new"))
771 new_entries[current].data.value = DEBUG_TYPE_NEW;
772 else if(!_wcsicmp(argv[i+1],L"reuse"))
773 new_entries[current].data.value = DEBUG_TYPE_REUSE;
774 else if(!_wcsicmp(argv[i+1],L"console"))
775 new_entries[current].data.value = DEBUG_TYPE_CONSOLE;
776 else {
777 fwprintf(stderr,L"%s: Unrecognized keyword value %s.\n",
778 argv[0],argv[i+1]);
779 return 1;
780 }
781 ++i;
782 }
783 break;
784 case Priority:
785 if(i+1 >= argc ||
786 *argv[i+1] == L'-' || *argv[i+1] == L'/'){
787 new_entries[current].data.value =
788 default_entries[current].data.value;
789 } else {
790 if(!_wcsicmp(argv[i+1],L"high"))
791 new_entries[current].data.value = HIGH_PRIORITY_CLASS;
792 else if(!_wcsicmp(argv[i+1],L"low"))
793 new_entries[current].data.value = IDLE_PRIORITY_CLASS;
794 else if(!_wcsicmp(argv[i+1],L"realtime"))
795 new_entries[current].data.value = REALTIME_PRIORITY_CLASS;
796 else {
797 fwprintf(stderr,L"%s: Unrecognized keyword value %s.\n",
798 argv[0],argv[i+1]);
799 return 1;
800 }
801 ++i;
802 }
803 break;
804
805 case Env:
806 if(i+1 >= argc ||
807 *argv[i+1] == L'-' || *argv[i+1] == L'/'){
808 fwprintf(stderr,L"%s: %s requires a parameter.\n",
809 argv[0],argv[i]);
810 return 1;
811 }
812 new_entries[current].data.string =
813 edit_env(argv[i+1], new_entries[current].data.string);
814 ++i;
815 break;
816 case InternalServiceName:
817 if (!add) {
818 fwprintf(stderr,L"%s: %s only allowed when adding a new service.\n",
819 argv[0],argv[i]);
820 return 1;
821 }
822 if(i+1 >= argc){
823 fwprintf(stderr,L"%s: %s requires a parameter.\n",
824 argv[0],argv[i]);
825 return 1;
826 }
827 new_entries[InternalServiceName].data.expand.unexpanded =
828 new_entries[InternalServiceName].data.string = argv[i+1];
829 ++i;
830 /* Discard old, should maybe be fred' but we'll exit anyway */
831 real_service_name = new_entries[InternalServiceName].data.string;
832 break;
833 default:
834 fwprintf(stderr,L"%s: Unrecognized option %s.\n", argv[0],
835 argv[i]);
836 return 1;
837 }
838 }
839 if(*new_entries[SName].data.string &&
840 *new_entries[Name].data.string){
841 #if 0
842 fwprintf(stderr,L"%s: Both -sname and -name specified.\n",
843 argv[0]);
844 return 1;
845 #else
846 new_entries[SName].data.string = L"";
847 #endif
848 }
849 if(add && !(*new_entries[SName].data.string) &&
850 !(*new_entries[Name].data.string)){
851 fwprintf(stderr,L"%s: Neither -sname nor -name specified.\n",
852 argv[0]);
853 return 1;
854 }
855
856 if(add && !install_service()){
857 fwprintf(stderr,L"%s: Unable to register %s service with service manager.\n",
858 argv[0], service_name);
859 print_last_error();
860 return 1;
861 }
862 if(!set_interactive(new_entries[DebugType].data.value ==
863 DEBUG_TYPE_CONSOLE)){
864 fwprintf(stderr,L"%s: Warning, could not set correct interactive mode. %s\n",
865 argv[0], service_name);
866 print_last_error();
867 /* Not severe or??? */
868 }
869 /* Update registry */
870 register_logkeys();
871 set_keys(service_name, new_entries);
872 /* Update service comment if needed */
873 if(set_comment) {
874 if (!set_service_comment(new_entries[Comment].data.string)) {
875 fwprintf(stderr,L"%s: Warning, could not set correct "
876 L"service description (comment) %s",
877 argv[0], service_name);
878 print_last_error();
879 }
880 }
881
882 /* As I do this, I should also clean up the new entries, which is
883 somewhat harder as I really dont know what is and what is not
884 malloced, but we'll exit anyway, so... */
885 cleanup_old();
886 if(add)
887 wprintf(L"%s: Service %s added to system.\n",
888 argv[0], service_name);
889 else
890 wprintf(L"%s: Service %s updated.\n",
891 argv[0], service_name);
892 return 0;
893 }
894
do_rename(int argc,wchar_t ** argv)895 int do_rename(int argc, wchar_t **argv){
896 RegEntry *current = empty_reg_tab();
897 RegEntry *dummy = empty_reg_tab();
898 SC_HANDLE scm;
899 SC_HANDLE service;
900 if(argc < 3){
901 fwprintf(stderr,L"%s: No old servicename given!\n",argv[0]);
902 do_usage(argv[0]);
903 return 1;
904 }
905 if(argc < 4){
906 fwprintf(stderr,L"%s: No new servicename given!\n",argv[0]);
907 do_usage(argv[0]);
908 return 1;
909 }
910 service_name = argv[3];
911 if(fetch_current(dummy)){
912 fwprintf(stderr,L"%s: A service with the name %s already "
913 L"exists.\n",
914 argv[0],service_name);
915 return 1;
916 }
917 service_name = argv[2];
918
919 if(!fetch_current(current)){
920 fwprintf(stderr,L"%s: Error, old service name %s does not exist.\n",
921 argv[0],service_name);
922 return 1;
923 }
924 real_service_name = _wcsdup(current[InternalServiceName].data.string);
925 if(!open_service_config(&scm,&service)){
926 fwprintf(stderr,L"%s: Error, unable to communicate with service control"
927 L" manager.\n",
928 argv[0]);
929 print_last_error();
930 return 1;
931 }
932 if(!ChangeServiceConfigW(service,
933 SERVICE_NO_CHANGE,
934 SERVICE_NO_CHANGE,
935 SERVICE_NO_CHANGE,
936 NULL,
937 NULL,
938 NULL,
939 NULL,
940 NULL,
941 NULL,
942 argv[3])){
943 fwprintf(stderr,L"%s: Error, unable to communicate with service control"
944 L" manager.\n",
945 argv[0]);
946 print_last_error();
947 CloseServiceHandle(scm);
948 CloseServiceHandle(service);
949 return 1;
950 }
951 CloseServiceHandle(scm);
952 CloseServiceHandle(service);
953
954 if(remove_keys(service_name) != 0)
955 fwprintf(stderr,L"%s: Warning, old service parameter keys could not "
956 L"be removed, continuing.\n", argv[0]);
957 /* Update registry */
958 register_logkeys();
959 set_keys(argv[3], current);
960
961 wprintf(L"%s: Service %s renamed to %s.\n",
962 argv[0], service_name, argv[3]);
963 return 0;
964 }
965
do_remove(int argc,wchar_t ** argv)966 int do_remove(int argc, wchar_t **argv){
967 RegEntry *current = empty_reg_tab();
968 int rem_res;
969 BOOL found;
970
971 if(argc < 3){
972 fwprintf(stderr,L"%s: No servicename given!\n",argv[0]);
973 do_usage(argv[0]);
974 return 1;
975 }
976 service_name = argv[2];
977 found = fetch_current(current);
978 if(found){
979 real_service_name = _wcsdup(current[InternalServiceName].data.string);
980 } else {
981 real_service_name = _wcsdup(service_name);
982 }
983 if(found)
984 free_keys(current);
985 if(stop_service() && !wait_service_trans(SERVICE_RUNNING,
986 SERVICE_STOP_PENDING,
987 SERVICE_STOPPED, 60)){
988 fwprintf(stderr,L"%s: Failed to stop running service %s.\n",
989 argv[0],service_name);
990 print_last_error();
991 return 1;
992 }
993 if(!remove_service()){
994 fwprintf(stderr,L"%s: Unable to remove service (not enough "
995 L"privileges?)\n",argv[0]);
996 print_last_error();
997 return 1;
998 }
999
1000 if((rem_res = remove_keys(service_name)) > 0){
1001 fwprintf(stderr,L"%s: Warning, service parameter keys belonged to old "
1002 L"erlsrv version.\n", argv[0]);
1003 /* Backward compatibility... */
1004 } else if(rem_res < 0) {
1005 fwprintf(stderr,L"%s: Error, service parameter keys nonexistent.\n",
1006 argv[0]);
1007 return 1;
1008 }
1009 wprintf(L"%s: Service %s removed from system.\n",
1010 argv[0], service_name);
1011 return 0;
1012 }
1013
list_one(wchar_t * servicename,RegEntry * keys,BOOL longlist)1014 BOOL list_one(wchar_t *servicename, RegEntry *keys, BOOL longlist){
1015 wchar_t *onfail;
1016 wchar_t *prio;
1017 wchar_t *debugtype;
1018 switch(keys[OnFail].data.value){
1019 case ON_FAIL_RESTART:
1020 onfail = L"restart";
1021 break;
1022 case ON_FAIL_RESTART_ALWAYS:
1023 onfail = L"restart_always";
1024 break;
1025 case ON_FAIL_REBOOT:
1026 onfail = L"reboot";
1027 break;
1028 default:
1029 onfail = L"ignore";
1030 }
1031 switch(keys[DebugType].data.value){
1032 case DEBUG_TYPE_NEW:
1033 debugtype = L"new";
1034 break;
1035 case DEBUG_TYPE_REUSE:
1036 debugtype = L"reuse";
1037 break;
1038 case DEBUG_TYPE_CONSOLE:
1039 debugtype = L"console";
1040 break;
1041 default:
1042 debugtype = L"none";
1043 }
1044 switch(keys[Priority].data.value){
1045 case HIGH_PRIORITY_CLASS:
1046 prio = L"high";
1047 break;
1048 case IDLE_PRIORITY_CLASS:
1049 prio = L"low";
1050 break;
1051 case REALTIME_PRIORITY_CLASS:
1052 prio = L"realtime";
1053 break;
1054 case NORMAL_PRIORITY_CLASS:
1055 prio = L"default";
1056 break;
1057 default:
1058 prio = L"unknown/faulty";
1059 }
1060
1061
1062 if(longlist){
1063 wchar_t *env = envdup(keys[Env].data.string);
1064 wchar_t **arg = env_to_arg(env);
1065 wchar_t **pek = arg;
1066 wprintf(L"Service name: %s\n",
1067 servicename);
1068 wprintf(L"StopAction: %s\n",
1069 keys[StopAction].data.string);
1070 wprintf(L"OnFail: %s\n",onfail);
1071 wprintf(L"Machine: %s\n",
1072 keys[Machine].data.expand.unexpanded);
1073 wprintf(L"WorkDir: %s\n",
1074 keys[WorkDir].data.expand.unexpanded);
1075 if(*keys[SName].data.string)
1076 wprintf(L"SName: %s\n",
1077 keys[SName].data.string);
1078 else
1079 wprintf(L"Name: %s\n",
1080 keys[Name].data.string);
1081 wprintf(L"Priority: %s\n",prio);
1082 wprintf(L"DebugType: %s\n",debugtype);
1083 wprintf(L"Args: %s\n",
1084 keys[Args].data.expand.unexpanded);
1085 wprintf(L"InternalServiceName: %s\n",
1086 keys[InternalServiceName].data.string);
1087 wprintf(L"Comment: %s\n",
1088 keys[Comment].data.string);
1089 wprintf(L"Env:\n");
1090 while(*pek){
1091 wprintf(L"\t%s\n",*pek);
1092 ++pek;
1093 }
1094 /* env is easier to free...*/
1095 env = arg_to_env(arg);
1096 free(env);
1097 } else {
1098 wprintf(L"%s\t%s\t%s\t%s\t%s\n",
1099 servicename,
1100 (*keys[Name].data.string) ?
1101 keys[Name].data.string :
1102 keys[SName].data.string,
1103 prio,
1104 onfail,
1105 keys[Args].data.expand.unexpanded);
1106 }
1107 return TRUE;
1108 }
1109
1110
do_list(int argc,wchar_t ** argv)1111 int do_list(int argc, wchar_t **argv){
1112 if(argc < 3){
1113 RegEntryDesc *all_keys = get_all_keys();
1114 if(!all_keys){
1115 fwprintf(stderr,L"%s: No services found in registry.\n",
1116 argv[0]);
1117 return 0;
1118 }
1119 wprintf(L"Service\t(S)Name\tPrio\tOnFail\tArgs\n");
1120 while(all_keys->servicename){
1121 list_one(all_keys->servicename,all_keys->entries,FALSE);
1122 ++all_keys;
1123 }
1124 return 0;
1125 } else {
1126 RegEntry *keys;
1127 service_name = argv[2];
1128 keys = get_keys(service_name);
1129 if(!keys){
1130 fwprintf(stderr,L"%s: Could not retrieve any "
1131 L"registered data for %s.\n",argv[0],service_name);
1132 return 1;
1133 }
1134 list_one(service_name, keys, TRUE);
1135 }
1136 return 0;
1137 }
1138
1139 #define READ_CHUNK 100
1140 #define ARGV_CHUNK 20
1141
safe_get_line(void)1142 wchar_t *safe_get_line(void){
1143 int lsize = READ_CHUNK;
1144 wchar_t *line = malloc(READ_CHUNK*sizeof(wchar_t));
1145 int pos = 0;
1146 int ch;
1147
1148 while((ch = getwchar()) != EOF && ch != L'\n'){
1149 if(pos + 1 >= lsize){
1150 line = realloc(line,(lsize += READ_CHUNK)*sizeof(wchar_t));
1151 assert(line);
1152 }
1153 line[pos++] = ch;
1154 }
1155 if(ch == EOF || !pos){
1156 free(line);
1157 return NULL;
1158 }
1159 line[pos] = L'\0';
1160 return line;
1161 }
1162
1163
read_arguments(int * pargc,wchar_t *** pargv)1164 void read_arguments(int *pargc, wchar_t ***pargv){
1165 int argc = 0;
1166 int asize = ARGV_CHUNK;
1167 wchar_t **argv = malloc(ARGV_CHUNK*sizeof(wchar_t *));
1168 wchar_t *tmp;
1169
1170 argv[0] = (*pargv)[0];
1171 argc = 1;
1172 while((tmp = safe_get_line()) != NULL){
1173 if(argc + 1 >= asize){
1174 argv = realloc(argv,(asize += ARGV_CHUNK)*sizeof(wchar_t *));
1175 assert(argv);
1176 }
1177 argv[argc++] = tmp;
1178 }
1179 argv[argc] = NULL;
1180 *pargc = argc;
1181 *pargv = argv;
1182 }
1183
1184 /* Create a free-for-all ACL to set on the semaphore */
get_acl(PSECURITY_DESCRIPTOR secdescp)1185 PACL get_acl(PSECURITY_DESCRIPTOR secdescp)
1186 {
1187 DWORD acl_length = 0;
1188 PSID auth_users_sidp = NULL;
1189 PACL aclp = NULL;
1190 SID_IDENTIFIER_AUTHORITY ntauth = SECURITY_NT_AUTHORITY;
1191
1192 if(!InitializeSecurityDescriptor(secdescp, SECURITY_DESCRIPTOR_REVISION)) {
1193 return NULL;
1194 }
1195
1196 if(!AllocateAndInitializeSid(&ntauth,
1197 1,
1198 SECURITY_AUTHENTICATED_USER_RID,
1199 0, 0, 0, 0, 0, 0, 0,
1200 &auth_users_sidp)) {
1201 return NULL;
1202 }
1203
1204 acl_length = sizeof(ACL) +
1205 sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) +
1206 GetLengthSid(auth_users_sidp);
1207
1208 if((aclp = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, acl_length)) == NULL) {
1209 FreeSid(auth_users_sidp);
1210 return NULL;
1211 }
1212
1213 if(!InitializeAcl(aclp, acl_length, ACL_REVISION)) {
1214 FreeSid(auth_users_sidp);
1215 HeapFree(GetProcessHeap(), 0, aclp);
1216 return NULL;
1217 }
1218
1219 if(!AddAccessAllowedAce(aclp, ACL_REVISION, SEMAPHORE_ALL_ACCESS, auth_users_sidp)) {
1220 FreeSid(auth_users_sidp);
1221 HeapFree(GetProcessHeap(), 0, aclp);
1222 return NULL;
1223 }
1224
1225 if(!SetSecurityDescriptorDacl(secdescp, TRUE, aclp, FALSE)) {
1226 FreeSid(auth_users_sidp);
1227 HeapFree(GetProcessHeap(), 0, aclp);
1228 return NULL;
1229 }
1230 return aclp;
1231 }
1232
1233 static HANDLE lock_semaphore = NULL;
1234
take_lock(void)1235 int take_lock(void) {
1236 SECURITY_ATTRIBUTES attr;
1237 PACL aclp;
1238 SECURITY_DESCRIPTOR secdesc;
1239
1240 if ((aclp = get_acl(&secdesc)) == NULL) {
1241 return -1;
1242 }
1243
1244 memset(&attr,0,sizeof(attr));
1245 attr.nLength = sizeof(attr);
1246 attr.lpSecurityDescriptor = &secdesc;
1247 attr.bInheritHandle = FALSE;
1248
1249 if ((lock_semaphore = CreateSemaphore(&attr, 1, 1, ERLSRV_INTERACTIVE_GLOBAL_SEMAPHORE)) == NULL) {
1250 return -1;
1251 }
1252
1253 if (WaitForSingleObject(lock_semaphore,INFINITE) != WAIT_OBJECT_0) {
1254 return -1;
1255 }
1256
1257 HeapFree(GetProcessHeap(), 0, aclp);
1258 return 0;
1259 }
1260
release_lock(void)1261 void release_lock(void) {
1262 ReleaseSemaphore(lock_semaphore,1,NULL);
1263 }
1264
1265
1266
interactive_main(int argc,wchar_t ** argv)1267 int interactive_main(int argc, wchar_t **argv){
1268 wchar_t *action = argv[1];
1269 int res;
1270
1271 _setmode(_fileno(stdin), _O_U8TEXT); /* set stdin to UTF8 */
1272 _setmode(_fileno(stdout), _O_U8TEXT); /* set stdout to UTF8 */
1273 _setmode(_fileno(stderr), _O_U8TEXT); /* set stderr to UTF8 */
1274
1275 if (take_lock() != 0) {
1276 fwprintf(stderr,L"%s: unable to acquire global lock (%S).\n",argv[0],
1277 ERLSRV_INTERACTIVE_GLOBAL_SEMAPHORE);
1278 return 1;
1279 }
1280
1281 if(!_wcsicmp(action,L"readargs")){
1282 read_arguments(&argc,&argv);
1283 action = argv[1];
1284 }
1285
1286 #ifdef HARDDEBUG
1287 {int i;
1288 for(i=0; i < argc; i++) {
1289 fwprintf(stderr, L"%s ", argv[i]);
1290 }
1291 fwprintf(stderr, L"\n");
1292 }
1293 #endif
1294
1295 if(!_wcsicmp(action,L"set") || !_wcsicmp(action,L"add"))
1296 res = do_add_or_set(argc,argv);
1297 else if(!_wcsicmp(action,L"rename"))
1298 res = do_rename(argc,argv);
1299 else if(!_wcsicmp(action,L"remove"))
1300 res = do_remove(argc,argv);
1301 else if(!_wcsicmp(action,L"list"))
1302 res = do_list(argc,argv);
1303 else if(!_wcsicmp(action,L"start") ||
1304 !_wcsicmp(action,L"start_disabled") ||
1305 !_wcsicmp(action,L"stop") ||
1306 !_wcsicmp(action,L"enable") ||
1307 !_wcsicmp(action,L"disable"))
1308 res = do_manage(argc,argv);
1309 else if(_wcsicmp(action,L"?") &&
1310 _wcsicmp(action,L"/?") &&
1311 _wcsicmp(action,L"-?") &&
1312 *action != L'h' &&
1313 *action != L'H') {
1314 fwprintf(stderr,L"%s: action %s not implemented.\n",argv[0],action);
1315 do_usage(argv[0]);
1316 res = 1;
1317 } else {
1318 do_usage(argv[0]);
1319 res = 0;
1320 }
1321 release_lock();
1322 return res;
1323 }
1324
1325