1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 /** \file wzd_main.c
27   * \brief Startup code: check args, load config file and start main thread.
28   */
29 /*! \addtogroup wzdftpd
30  *  \brief Main executable group
31  *  @{
32  */
33 
34 /* Sanity check */
35 #ifdef WZD_MULTIPROCESS
36 #ifdef WZD_MULTITHREAD
37 
38 #error "You CAN'T have a multi-thread multi-process server, stupid !"
39 
40 #endif /* WZD_MULTITHREAD */
41 #endif /* WZD_MULTIPROCESS */
42 
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 
51 #ifdef WIN32
52 #include <winsock2.h>
53 
54 #include "../visual/gnu_regex/regex.h"
55 
56 #include <Windows.h>
57 #else
58 #include <unistd.h>
59 
60 #include <sys/types.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 
65 #include <regex.h>
66 
67 #include <syslog.h>
68 
69 #if defined(__sun__)
70 # define LOG_FTP LOG_DAEMON
71 #endif
72 
73 #endif
74 
75 #include <errno.h>
76 #include <fcntl.h>
77 
78 #include <libwzd-core/wzd_structs.h>
79 
80 #include <libwzd-core/wzd_misc.h>
81 #include <libwzd-core/wzd_log.h>
82 #include <libwzd-core/wzd_messages.h>
83 #include <libwzd-core/wzd_tls.h>
84 #include <libwzd-core/wzd_configfile.h>
85 #include <libwzd-core/wzd_configloader.h>
86 #include <libwzd-core/wzd_libmain.h>
87 #include <libwzd-core/wzd_utf8.h>
88 
89 #include "wzd_opts.h"
90 #include "wzd_ServerThread.h"
91 
92 #include <libwzd-core/wzd_debug.h>
93 
94 #include "wzd_version.h"
95 
96 #ifdef WIN32
97 int nt_service_register(void);
98 int nt_service_unregister(void);
99 int nt_service_start(void);
100 int nt_service_stop(void);
101 int nt_is_service(void);
102 
103 VOID UpdateSCM( DWORD dwCurrentState , DWORD dwWaitHint , DWORD dwWin32ExitCode );
104 VOID MyServiceRun(DWORD argc, LPSTR *argv);
105 VOID MyServiceCtrlHandler(DWORD opcode);
106 
107 void SvcDebugOut(const char *fmt,...);
108 
109 SERVICE_STATUS              service_status;
110 SERVICE_STATUS_HANDLE       service_status_handle;
111 
112 static int ntservice=0;
113 #endif
114 
115 typedef enum {
116   CMD_NONE=0,
117 #ifdef WIN32
118   CMD_SRV_REGISTER,
119   CMD_SRV_UNREGISTER,
120   CMD_SRV_START,
121   CMD_SRV_STOP,
122 #endif
123   CMD_TEST_CONFIG,
124 } wzd_arg_command_t;
125 
126 char configfile_name[256];
127 int stay_foreground=0;
128 static wzd_arg_command_t start_command=CMD_NONE;
129 
130 static const char * config_files[] = {
131   "",
132   WZD_DEFAULT_CONF,
133   "wzd.cfg",
134   "/etc/wzdftpd/wzd.cfg",
135   "/etc/wzd.cfg",
136   "/usr/local/etc/wzd.cfg",
137   NULL /* do NOT remove */
138 };
139 
display_usage(void)140 void display_usage(void)
141 {
142   fprintf(stderr,"%s build %s (%s)\n", WZD_VERSION_LONG,WZD_BUILD_NUM,WZD_BUILD_OPTS);
143   fprintf(stderr, "\nusage: wzdftpd [arguments]\n");
144   fprintf(stderr,"\narguments:\r\n");
145 #ifdef HAVE_GETOPT_LONG
146   fprintf(stderr," -h, --help                  - Display this text \n");
147 #if DEBUG
148   fprintf(stderr," -b, --background            - Force background \n");
149 #endif
150   fprintf(stderr," -f <file>                   - Load alternative config file \n");
151   fprintf(stderr," -s, --force-foreground      - Stay in foreground \n");
152   fprintf(stderr," -t, --configtest            - Test configuration file\n");
153   fprintf(stderr," -V, --version               - Show version \n");
154 #else /* HAVE_GETOPT_LONG */
155   fprintf(stderr," -h                          - Display this text \n");
156 #if DEBUG
157   fprintf(stderr," -b                          - Force background \n");
158 #endif
159   fprintf(stderr," -f <file>                   - Load alternative config file \n");
160   fprintf(stderr," -s                          - Stay in foreground \n");
161 #ifdef WIN32
162   fprintf(stderr," -si                         - Register service \n");
163   fprintf(stderr," -sd                         - Unregister service \n");
164   fprintf(stderr," -ss                         - Start service (must be registered) \n");
165   fprintf(stderr," -st                         - Stop service (must be registered) \n");
166 #endif
167   fprintf(stderr," -V                          - Show version \n");
168 
169 #endif /* HAVE_GETOPT_LONG */
170 }
171 
172 
main_parse_args(int argc,char ** argv)173 int main_parse_args(int argc, char **argv)
174 {
175 #ifndef WIN32
176   int opt;
177 
178 
179 #ifdef HAVE_GETOPT_LONG
180   static struct option long_options[] =
181   {
182     /* Options without arguments: */
183     { "background", no_argument, NULL, 'b' },
184     { "config-file", required_argument, NULL, 'f' },
185     { "help", no_argument, NULL, 'h' },
186     { "force-foreground", no_argument, NULL, 's' },
187     { "configtest", no_argument, NULL, 't' },
188     { "version", no_argument, NULL, 'V' },
189     { NULL, 0, NULL, 0 } /* sentinel */
190   };
191 
192   /* please keep options ordered ! */
193 /*  while ((opt=getopt(argc, argv, "hbdf:sV")) != -1) {*/
194   while ((opt=getopt_long(argc, argv, "hbf:stV", long_options, (int *)0)) != -1)
195 #else /* HAVE_GETOPT_LONG */
196   while ((opt=getopt(argc, argv, "hbf:stV")) != -1)
197 #endif /* HAVE_GETOPT_LONG */
198   {
199     switch((char)opt) {
200     case 'b':
201       stay_foreground = 0;
202       break;
203     case 'f':
204       if (strlen(optarg)>=255) {
205         fprintf(stderr,"filename too long (>255 chars)\n");
206         return 1;
207       }
208       strncpy(configfile_name,optarg,255);
209       break;
210     case 'h':
211       display_usage();
212       exit (0);
213     case 's':
214       stay_foreground = 1;
215       break;
216     case 't':
217       start_command = CMD_TEST_CONFIG;
218       break;
219     case 'V':
220       fprintf(stderr,"%s build %s (%s)\n",
221           WZD_VERSION_LONG,WZD_BUILD_NUM,WZD_BUILD_OPTS);
222       exit (0);
223     }
224   }
225 #else /* WIN32 */
226   if (argc > 1) {
227     int optindex=1;
228     while (optindex < argc) {
229       if (!strcmp(argv[optindex],"-f")) {
230         optindex++;
231         if (optindex < argc) {
232           if (strlen(argv[optindex])>=255) {
233             fprintf(stderr,"filename too long (>255 chars)\n");
234             return 1;
235           }
236           strncpy(configfile_name,argv[optindex],255);
237           optindex++;
238         } else {
239           fprintf(stderr,"missing filename after -f option\n");
240           return 1;
241         }
242         continue;
243       }
244       if (!strcmp(argv[optindex],"-b")) {
245         stay_foreground = 0;
246         optindex++;
247         continue;
248       }
249       if (!strcmp(argv[optindex],"-s")) {
250         stay_foreground = 1;
251         optindex++;
252         continue;
253       }
254       if (!strcmp(argv[optindex],"-h") || !strcmp(argv[optindex],"--help")
255 		  || !strcmp(argv[optindex],"/?") || !strcmp(argv[optindex],"-?")) {
256         display_usage();
257         exit (0);
258       }
259       if (!strcmp(argv[optindex],"-V")) {
260         fprintf(stderr,"%s build %s (%s)\n",
261             WZD_VERSION_LONG,WZD_BUILD_NUM,WZD_BUILD_OPTS);
262         exit (0);
263       }
264       if (!strcmp(argv[optindex],"-si")) {
265         start_command = CMD_SRV_REGISTER;
266         optindex++;
267         continue;
268       }
269       if (!strcmp(argv[optindex],"-sd")) {
270         start_command = CMD_SRV_UNREGISTER;
271         optindex++;
272         continue;
273       }
274       if (!strcmp(argv[optindex],"-ss")) {
275         start_command = CMD_SRV_START;
276         optindex++;
277         continue;
278       }
279       if (!strcmp(argv[optindex],"-st")) {
280         start_command = CMD_SRV_STOP;
281         optindex++;
282         continue;
283       }
284       if (!strcmp(argv[optindex],"-t")) {
285         start_command = CMD_TEST_CONFIG;
286         optindex++;
287         continue;
288       }
289       if (!strcmp(argv[optindex],"-service")) {
290         ntservice = 1;
291         optindex++;
292         continue;
293       }
294       break;
295     }
296   }
297 #endif /* WIN32 */
298   return 0;
299 }
300 
301 
302 
main(int argc,char ** argv)303 int main(int argc, char **argv)
304 {
305   int ret, i;
306   pid_t forkresult;
307   wzd_config_t * config;
308   wzd_configfile_t * cf;
309 
310   /* register wzdftpd mutex so the installer/other programs can check if wzdftpd is active */
311 #ifdef WIN32
312   CreateMutex(NULL,FALSE,"wzdftpdIsActive");
313   CreateMutex(NULL,FALSE,"Global\\wzdftpdIsActive");
314 #endif
315 
316   wzd_debug_init();
317 
318 
319 #if 0
320   fprintf(stderr,"--------------------------------------\n");
321   fprintf(stderr,"\n");
322   fprintf(stderr,"This is a beta release, in active development\n");
323   fprintf(stderr,"Things may break from version to version\n");
324   fprintf(stderr,"Want stability ? Use a 0.4 version. YOU WERE WARNED!\n");
325   fprintf(stderr,"\n");
326   fprintf(stderr,"--------------------------------------\n");
327   fprintf(stderr,"\n");
328 #endif
329 
330 #if DEBUG
331   stay_foreground = 1;
332 #endif
333   /* default value */
334 /*  strcpy(configfile_name,"wzd.cfg");*/
335   configfile_name[0]='\0';
336 
337   if (argc > 1) {
338     ret = main_parse_args(argc,argv);
339     if (ret) {
340       out_err(LEVEL_CRITICAL,"Error while parsing args, aborting\n");
341       return 0;
342     }
343     config_files[0] = configfile_name;
344 
345     switch (start_command) {
346 #ifdef WIN32
347       case CMD_SRV_UNREGISTER:
348         nt_service_unregister();
349         exit (0);
350       case CMD_SRV_START:
351         nt_service_start();
352         exit (0);
353       case CMD_SRV_STOP:
354         nt_service_stop();
355         exit (0);
356 #endif
357       case CMD_TEST_CONFIG:
358         {
359           const char * test_config = NULL;
360           /* try new config file format first */
361           cf = config_new();
362           for (i=0; config_files[i]; i++) {
363             if (config_files[i][0]!='\0') { test_config = config_files[i]; break; }
364           }
365           if (test_config == NULL) {
366             out_err(LEVEL_CRITICAL,"Could not find ANY config file !\n");
367             out_err(LEVEL_CRITICAL,"Try restarting with command -f <config>\n");
368             exit (1);
369           }
370           out_err(LEVEL_NORMAL,"Testing configuration file %s\n",test_config);
371           ret = config_load_from_file (cf, test_config, 0);
372           if (!ret) {
373             int err;
374 
375             out_err(LEVEL_INFO,"config: NEW format found [%s]\n",config_files[i]);
376 
377             config = cfg_store(cf,&err);
378             if (config) {
379               out_err(LEVEL_NORMAL,"*** Configuration test OK ***\n");
380               exit (0);
381             }
382           }
383           out_err(LEVEL_CRITICAL,"ERROR: could NOT load config file %s\n",test_config);
384           config_free(cf);
385         }
386         exit (-1);
387       default:
388         break;
389     }
390   }
391 
392   if (!stay_foreground) {
393 #ifndef WIN32
394     forkresult = fork();
395 #else
396     forkresult = 0;
397 #endif
398 
399     if ((int)forkresult == -1)
400       fprintf(stderr,"Could not fork into background\n");
401     if ((int)forkresult != 0)
402       exit(0);
403   }
404 
405   /* initialize random seed */
406   srand((unsigned int)(time(NULL)+0x13313043));
407 
408   /* not really usefull, but will also initialize var if not used :) */
409 #ifndef WIN32
410   setlib_server_uid(geteuid());
411 #endif
412 
413   /* initialize logging facilities */
414   if (log_init()) {
415     fprintf(stderr,"FATAL: Couldn't init logging facilities, aborting\n");
416     exit(1);
417   }
418 
419   /* default server messages */
420   init_default_messages();
421 
422   /* config file */
423   config = NULL;
424 
425   for (i=0; config_files[i]; i++)
426   {
427     /* try new config file format first */
428     cf = config_new();
429     ret = config_load_from_file (cf, config_files[i], 0);
430     if (!ret) {
431       int err;
432 
433       out_err(LEVEL_INFO,"config: NEW format found [%s]\n",config_files[i]);
434 
435       config = cfg_store(cf,&err);
436       if (config) {
437         /* cf will NOT be freed at this point, it is stored into config */
438         break;
439       }
440     }
441     config_free(cf);
442     if (!ret) break;
443   }
444 
445   if (!config) {
446     fprintf(stderr,"FATAL: No valid config file found, aborting!\n");
447     out_err(LEVEL_CRITICAL,"FATAL: Critical error loading config file, aborting\n");
448     exit(1);
449   }
450 
451   config->config_filename = wzd_strdup(config_files[i]);
452 
453 
454   /* \todo XXX use values given in command-line ? */
455   switch (start_command) {
456     case CMD_NONE:
457       break;
458 #ifdef WIN32
459     case CMD_SRV_REGISTER:
460       mainConfig = config;
461       nt_service_register();
462       exit (0);
463     case CMD_SRV_UNREGISTER:
464       mainConfig = config;
465       nt_service_unregister();
466       exit (0);
467     case CMD_SRV_START:
468       mainConfig = config;
469       nt_service_start();
470       exit (0);
471     case CMD_SRV_STOP:
472       mainConfig = config;
473       nt_service_stop();
474       exit (0);
475 #endif
476     default:
477       break;
478   }
479 
480 
481   mainConfig = config;
482   setlib_mainConfig(mainConfig);
483 
484 #ifndef WIN32
485   if (CFG_GET_OPTION(mainConfig,CFG_OPT_USE_SYSLOG)) {
486     openlog("wzdftpd", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_FTP);
487     // LOG_CONS - If syslog could not pass our messages they'll apear on console,
488     // LOG_NDELAY - We don't want to wait for first message but open the connection to syslogd immediatly
489     // LOG_PID - We want see pid of of deamon in logfiles (Is it needed?)
490     for (i=0; i<MAX_LOG_CHANNELS; i++)
491       log_set_syslog(i,1);
492   }
493 #endif
494   if (mainConfig->logfilename != NULL) {
495     ret = log_open(mainConfig->logfilename,mainConfig->logfilemode);
496     if (ret < 0) {
497       /* stderr is not closed here, even in release mode */
498       fprintf(stderr,"FATAL: Could not open log file %s\n",mainConfig->logfilename);
499       return 1;
500     }
501     /** \todo this should be removed (as well as log_get() function) and replace
502      * with a proper init code
503      */
504     for (i=0; i<MAX_LOG_CHANNELS; i++) {
505       if (log_get(i) == -1)
506         log_set(i,ret);
507     }
508   }
509 
510 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
511   ret = tls_init();
512   if (ret) {
513     out_log(LEVEL_CRITICAL,"TLS subsystem could not be initialized.\n");
514     return 1;
515   }
516 #endif
517 
518   utf8_detect(mainConfig);
519 
520 #if defined(DEBUG) || !defined(WIN32)
521   ret = runMainThread(argc,argv);
522 #else
523   if (ntservice)
524   {
525     SERVICE_TABLE_ENTRY DispatchTable[] = {
526       { "wzdftpd", (LPSERVICE_MAIN_FUNCTION)MyServiceRun },
527       { NULL, NULL }
528     };
529     if (!StartServiceCtrlDispatcher(DispatchTable))
530       SvcDebugOut( "[wzdftpd] StartServiceCtrlDispatcher error = %d\n", GetLastError());
531   }
532   else
533     ret = runMainThread(argc,argv);
534 #endif
535 
536   /* we should never pass here - see wzd_ServerThread.c */
537 
538   return ret;
539 }
540 
541 #ifdef WIN32
542 
543 /** \brief Report the status to the service manager */
UpdateSCM(DWORD dwCurrentState,DWORD dwWaitHint,DWORD dwWin32ExitCode)544 VOID UpdateSCM(DWORD dwCurrentState, DWORD dwWaitHint, DWORD dwWin32ExitCode)
545 {
546   DWORD status;
547 
548   service_status.dwServiceType = SERVICE_WIN32;
549   service_status.dwCurrentState = dwCurrentState;
550   service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
551   service_status.dwWin32ExitCode = dwWin32ExitCode;
552   service_status.dwServiceSpecificExitCode = 0;
553   service_status.dwCheckPoint = 0;
554   service_status.dwWaitHint = dwWaitHint;
555 
556   /* report status */
557   if (!SetServiceStatus(service_status_handle, &service_status)){
558     status = GetLastError();
559     SvcDebugOut(" [wzdftpd] SetServiceStatus %d error %d\n",dwCurrentState,GetLastError() );
560   }
561 }
562 
MyServiceRun(DWORD argc,LPSTR * argv)563 VOID MyServiceRun(DWORD argc, LPSTR *argv)
564 {
565   int ret;
566 
567   service_status_handle = RegisterServiceCtrlHandler(  "wzdftpd",  (LPHANDLER_FUNCTION)MyServiceCtrlHandler);
568   if (!service_status_handle){
569     SvcDebugOut( "[wzdftpd] RegisterServiceCtrlHandler error = %d\n", GetLastError() );
570     return;
571   }
572 
573   /* report start pending status */
574   UpdateSCM( SERVICE_START_PENDING , 0 , 0 );
575 
576   /* Actually there should pass some time/stuff between reporting the pending and running status */
577   UpdateSCM( SERVICE_RUNNING , 0 , 0 );
578   /* This is where the service does its work */
579   SvcDebugOut("[wzdftpd] Going to run main thread\n");
580   ret = runMainThread(argc,argv);
581 
582   SvcDebugOut("[wzdftpd] Reported SERVICE_STOPPED\n");
583   /* report stopped status */
584   UpdateSCM( SERVICE_STOPPED , 0, 0 );
585 }
586 
MyServiceCtrlHandler(DWORD opcode)587 VOID MyServiceCtrlHandler(DWORD opcode)
588 {
589   DWORD status;
590 
591   switch(opcode)
592   {
593     case SERVICE_CONTROL_PAUSE:
594       break;
595     case SERVICE_CONTROL_CONTINUE:
596       break;
597     case SERVICE_CONTROL_STOP:
598       mainConfig->serverstop = 1;
599       UpdateSCM( SERVICE_STOP_PENDING , 5000 , 0 );
600       SvcDebugOut(" [wzdftpd] exiting\n",0);
601       return;
602     case SERVICE_CONTROL_INTERROGATE:
603       /* fall through to send current status */
604       break;
605     default:
606       SvcDebugOut(" [wzdftpd] Unrecognized opcode %ld\n",opcode);
607       break;
608   }
609 
610   /* send current status */
611   if (!SetServiceStatus(service_status_handle,&service_status)) {
612     status = GetLastError();
613     SvcDebugOut(" [wzdftpd] SetServiceStatus error %ld\n",status);
614   }
615 }
616 
nt_service_register(void)617 int nt_service_register(void)
618 {
619   SC_HANDLE schService, schSCManager;
620   LPCTSTR binaryPathName;
621   char buffer[MAX_PATH+1];
622   char config_fullpath[MAX_PATH+1];
623   char startcmd[MAX_PATH+1];
624 
625   if ( !mainConfig || !mainConfig->config_filename ||
626     !_fullpath(config_fullpath,mainConfig->config_filename,MAX_PATH) ) {
627     fprintf(stderr,"fullpath failed %d\n",GetLastError());
628     return -1;
629   }
630 
631   /* obtain a handler to the SC Manager database */
632   schSCManager = OpenSCManager(
633       NULL,     /* local machine */
634       NULL,     /* ServicesActive database */
635       SC_MANAGER_ALL_ACCESS); /* full access rights */
636 
637   if (schSCManager == NULL) return -1;
638 
639   GetModuleFileName(NULL,buffer,sizeof(buffer));
640   binaryPathName = buffer;
641 
642   snprintf(startcmd,MAX_PATH,"%s -f \"%s\" -service",binaryPathName,config_fullpath);
643 
644   schService = CreateService(
645       schSCManager,             /* SCManager database */
646       "wzdftpd",                /* name of service */
647       "wzdftpd",                /* service name to display */
648       SERVICE_ALL_ACCESS,       /* desired access */
649       SERVICE_WIN32_OWN_PROCESS,/* service type */
650       SERVICE_DEMAND_START,     /* start type */
651       SERVICE_ERROR_NORMAL,     /* error control type */
652       startcmd,                 /* service's binary */
653       NULL,                     /* no load ordering group */
654       NULL,                     /* no tag identifier */
655       NULL,                     /* no dependancies */
656       NULL,                     /* LocalSystem account */
657       NULL);                    /* no password */
658 
659   if (schService == NULL) {
660     fprintf(stderr,"CreateService failed %d\n",GetLastError());
661     CloseServiceHandle(schSCManager);
662     return -1;
663   }
664 
665   CloseServiceHandle(schService);
666   CloseServiceHandle(schSCManager);
667 
668   return 0;
669 }
670 
nt_service_unregister(void)671 int nt_service_unregister(void)
672 {
673   SC_HANDLE schService, schSCManager;
674 
675   /* obtain a handler to the SC Manager database */
676   schSCManager = OpenSCManager(
677       NULL,     /* local machine */
678       NULL,     /* ServicesActive database */
679       SC_MANAGER_ALL_ACCESS); /* full access rights */
680 
681   if (schSCManager == NULL) return -1;
682 
683   schService = OpenService(
684       schSCManager,             /* SCManager database */
685       "wzdftpd",                /* name of service */
686       DELETE);                  /* only need DELETE access */
687 
688   if (schService == NULL) {
689     fprintf(stderr,"OpenService failed %d\n",GetLastError());
690     CloseServiceHandle(schSCManager);
691     return -1;
692   }
693 
694   if (!DeleteService(schService)) {
695     fprintf(stderr,"DeleteService failed %d\n",GetLastError());
696     CloseServiceHandle(schSCManager);
697     return -1;
698   }
699 
700   CloseServiceHandle(schService);
701   CloseServiceHandle(schSCManager);
702 
703   return 0;
704 }
705 
nt_service_start(void)706 int nt_service_start(void)
707 {
708   SC_HANDLE schService, schSCManager;
709   SERVICE_STATUS ssStatus;
710   DWORD dwOldCheckPoint;
711   DWORD dwStartTickCount;
712   DWORD dwWaitTime;
713 
714   /* obtain a handler to the SC Manager database */
715   schSCManager = OpenSCManager(
716       NULL,     /* local machine */
717       NULL,     /* ServicesActive database */
718       SC_MANAGER_ALL_ACCESS); /* full access rights */
719 
720   if (schSCManager == NULL) return -1;
721 
722   schService = OpenService(
723       schSCManager,             /* SCManager database */
724       "wzdftpd",                /* name of service */
725       SERVICE_ALL_ACCESS);
726 
727   if (schService == NULL) {
728     fprintf(stderr,"OpenService failed %d\n",GetLastError());
729     CloseServiceHandle(schSCManager);
730     return -1;
731   }
732 
733   if (!StartService(
734         schService,     /* handle to service */
735         0,              /* number of arguments */
736         NULL))          /* no arguments */
737   {
738     fprintf(stderr,"Service started\n");
739     CloseServiceHandle(schService);
740     CloseServiceHandle(schSCManager);
741     return 0;
742   } else {
743     fprintf(stderr,"Service start pending\n");
744 
745     /* check the status until the service is no longer start pending */
746     if (!QueryServiceStatus(
747           schService,   /* handle to service */
748           &ssStatus))   /* address of status information structure */
749     {
750       CloseServiceHandle(schSCManager);
751       return -1;
752     }
753 
754     /* save the tick count and initial checkpoint */
755     dwStartTickCount = GetTickCount();
756     dwOldCheckPoint = ssStatus.dwCheckPoint;
757 
758     while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
759     {
760       /* do not wait longer than the wait hint. A good interval is
761        * one tenth the wait hint, but no less than 1 second and no
762        * more than 10 seconds
763        */
764       dwWaitTime = ssStatus.dwWaitHint / 10;
765       if (dwWaitTime < 1000)
766         dwWaitTime = 1000;
767       else if (dwWaitTime > 10000)
768         dwWaitTime = 10000;
769 
770       Sleep(dwWaitTime);
771 
772       /* check the status again */
773       if (!QueryServiceStatus(
774             schService,   /* handle to service */
775             &ssStatus))   /* address of status information structure */
776         break;
777 
778       fprintf(stderr,".");
779       fflush(stderr);
780 
781       if (ssStatus.dwCheckPoint > dwOldCheckPoint)
782       {
783         /* the service is making progress */
784         dwStartTickCount = GetTickCount();
785         dwOldCheckPoint = ssStatus.dwCheckPoint;
786       } else {
787         if (GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
788         {
789           /* no progress made withiin the wait hint */
790           break;
791         }
792       }
793     }
794 
795   }
796 
797   CloseServiceHandle(schService);
798   CloseServiceHandle(schSCManager);
799 
800   if (ssStatus.dwCurrentState == SERVICE_RUNNING)
801   {
802     fprintf(stderr,"Service started.\n");
803   } else {
804     fprintf(stderr,"Service not started\n");
805   }
806 
807   return 0;
808 }
809 
nt_service_stop(void)810 int nt_service_stop(void)
811 {
812   SC_HANDLE schService, schSCManager;
813   SERVICE_STATUS ssStatus;
814   DWORD dwStartTime;
815   DWORD dwTimeout;
816 
817   /* obtain a handler to the SC Manager database */
818   schSCManager = OpenSCManager(
819       NULL,     /* local machine */
820       NULL,     /* ServicesActive database */
821       SC_MANAGER_ALL_ACCESS); /* full access rights */
822 
823   if (schSCManager == NULL) return -1;
824 
825   schService = OpenService(
826       schSCManager,             /* SCManager database */
827       "wzdftpd",                /* name of service */
828       SERVICE_ALL_ACCESS);
829 
830   if (schService == NULL) {
831     fprintf(stderr,"OpenService failed %d\n",GetLastError());
832     CloseServiceHandle(schSCManager);
833     return -1;
834   }
835   dwStartTime = GetTickCount();
836   dwTimeout = 10000; /* 10s */
837 
838   if (!QueryServiceStatus( schService, &ssStatus))
839     return GetLastError();
840 
841   if (ssStatus.dwCurrentState == SERVICE_STOPPED) {
842     fprintf(stderr,"Service already stopped\n");
843     return 0;
844   }
845 
846   /* if a stop is pending, just wait for it */
847   while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
848   {
849     Sleep(ssStatus.dwWaitHint);
850 
851     if (!QueryServiceStatus( schService, &ssStatus))
852       return GetLastError();
853 
854     if (GetTickCount()-dwStartTime > dwTimeout) {
855       fprintf(stderr,"Timeout\n");
856       return -1;
857     }
858   }
859 
860   if (!ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus))
861     return GetLastError();
862 
863   /* wait for the service to stop */
864   while (ssStatus.dwCurrentState != SERVICE_STOPPED)
865   {
866     Sleep(ssStatus.dwWaitHint);
867 
868     if (!QueryServiceStatus( schService, &ssStatus))
869       return GetLastError();
870 
871     if (GetTickCount()-dwStartTime > dwTimeout) {
872       fprintf(stderr,"Timeout\n");
873       return -1;
874     }
875   }
876 
877   CloseServiceHandle(schService);
878   CloseServiceHandle(schSCManager);
879 
880   if (ssStatus.dwCurrentState == SERVICE_STOPPED)
881   {
882     fprintf(stderr,"Service stopped.\n");
883   } else {
884     fprintf(stderr,"Service not stopped\n");
885   }
886 
887   return 0;
888 }
889 
SvcDebugOut(const char * fmt,...)890 void SvcDebugOut(const char *fmt,...)
891 {
892   va_list argptr;
893   char buffer[4096];
894 
895   va_start(argptr,fmt);
896   vsnprintf(buffer,1024,fmt,argptr);
897   va_end(argptr);
898 
899   OutputDebugStringA(buffer);
900 }
901 
902 #endif /* WIN32 */
903 
904 /*! @} */
905