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