2 #define CONFIG "config.h"
6 #if defined(USE_MSRPC) && !defined(_WIN32) && !defined(__CYGWIN__)
7 #error Microsoft RPC is only available on Windows and Cygwin
10 #if defined(NO_SOCKETS) && defined(USE_MSRPC)
11 #error Cannot use inetd mode with Microsoft RPC
27 #include <sys/types.h>
29 #if !defined(NO_LIMIT) && !__minix__
34 #include <sys/syscall.h>
36 #endif // !defined(NO_LIMIT) && !__minix__
42 #include <semaphore.h>
46 #include <mach-o/dyld.h>
49 #if __linux__ && defined(USE_AUXV)
54 #include <sys/sysctl.h>
59 #include "shared_globals.h"
64 #include "msrpc-server.h"
66 #include "ntservice.h"
70 static const char* const optstring
= "N:B:m:t:w:0:3:H:A:R:u:g:L:p:i:P:l:r:U:W:C:SsfeDd46VvIdqkZ";
72 #if !defined(NO_SOCKETS)
73 #if !defined(USE_MSRPC)
74 static uint_fast8_t maxsockets
= 0;
75 static int_fast8_t haveIPv6Stack
= 0;
76 static int_fast8_t haveIPv4Stack
= 0;
77 static int_fast8_t v6required
= 0;
78 static int_fast8_t v4required
= 0;
79 #endif // !defined(USE_MSRPC)
80 #endif // !defined(NO_SOCKETS)
83 static int_fast8_t installService
= 0;
84 static const char *restrict ServiceUser
= NULL
;
85 static const char *restrict ServicePassword
= "";
89 static const char *fn_pid
= NULL
;
95 static const char *fn_ini
= INI_FILE
;
97 static const char *fn_ini
= NULL
;
100 static const char* IniFileErrorMessage
= "";
101 char* IniFileErrorBuffer
= NULL
;
102 #define INIFILE_ERROR_BUFFERSIZE 256
104 static IniFileParameter_t IniFileParameterList
[] =
106 # ifndef NO_RANDOM_EPID
107 { "RandomizationLevel", INI_PARAM_RANDOMIZATION_LEVEL
},
108 { "LCID", INI_PARAM_LCID
},
109 # endif // NO_RANDOM_EPID
111 { "Port", INI_PARAM_PORT
},
113 # if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
114 { "Listen", INI_PARAM_LISTEN
},
115 # if !defined(NO_LIMIT) && !__minix__
116 { "MaxWorkers", INI_PARAM_MAX_WORKERS
},
117 # endif // !defined(NO_LIMIT) && !__minix__
118 # endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC)
119 # if !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC) & !defined(USE_MSRPC)
120 { "ConnectionTimeout", INI_PARAM_CONNECTION_TIMEOUT
},
121 # endif // !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC) & !defined(USE_MSRPC)
123 { "DisconnectClientsImmediately", INI_PARAM_DISCONNECT_IMMEDIATELY
},
124 { "UseNDR64", INI_PARAM_RPC_NDR64
},
125 { "UseBTFN", INI_PARAM_RPC_BTFN
},
128 { "PIDFile", INI_PARAM_PID_FILE
},
129 # endif // NO_PID_FILE
131 { "LogFile", INI_PARAM_LOG_FILE
},
132 # ifndef NO_VERBOSE_LOG
133 { "LogVerbose", INI_PARAM_LOG_VERBOSE
},
134 # endif // NO_VERBOSE_LOG
136 # ifndef NO_CUSTOM_INTERVALS
137 {"ActivationInterval", INI_PARAM_ACTIVATION_INTERVAL
},
138 {"RenewalInterval", INI_PARAM_RENEWAL_INTERVAL
},
139 # endif // NO_CUSTOM_INTERVALS
140 # if !defined(NO_USER_SWITCH) && !defined(_WIN32)
141 { "user", INI_PARAM_UID
},
142 { "group", INI_PARAM_GID
},
143 # endif // !defined(NO_USER_SWITCH) && !defined(_WIN32)
146 #endif // NO_INI_FILE
149 #if !defined(NO_LIMIT) && !defined (NO_SOCKETS) && !__minix__
151 #if !defined(USE_THREADS) && !defined(CYGWIN) && !defined(USE_MSRPC)
152 static int shmid
= -1;
155 #if __ANDROID__ && !defined(USE_THREADS) // Bionic does not wrap these syscalls (willingly because Google fears, developers don't know how to use it)
158 static int shmget(key_t key
, size_t size
, int shmflg
)
160 return syscall(__NR_shmget
, key
, size
, shmflg
);
162 #endif // __NR_shmget
165 static void *shmat(int shmid
, const void *shmaddr
, int shmflg
)
167 return (void *)syscall(__NR_shmat
, shmid
, shmaddr
, shmflg
);
172 static int shmdt(const void *shmaddr
)
174 return syscall(__NR_shmdt
, shmaddr
);
179 static int shmctl(int shmid
, int cmd
, /*struct shmid_ds*/void *buf
)
181 return syscall(__NR_shmctl
, shmid
, cmd
, buf
);
183 #endif // __NR_shmctl
185 #endif // __ANDROID__ && !defined(USE_THREADS)
187 #endif // !defined(NO_LIMIT) && !defined (NO_SOCKETS) && !__minix__
189 #ifndef NO_USER_SWITCH
192 static const char *uname
= NULL
, *gname
= NULL
;
193 static gid_t gid
= INVALID_GID
;
194 static uid_t uid
= INVALID_UID
;
196 // Get Numeric id of user/group
197 static char GetNumericId(gid_t
*restrict id
, const char *const c
)
202 temp
= (gid_t
)strtoll(c
, &endptr
, 10);
204 if (!*endptr
) *id
= temp
;
210 // Get group id from option argument
215 if ((g
= getgrnam(optarg
)))
218 return GetNumericId(&gid
, optarg
);
224 // Get user id from option argument
229 ////PORTABILITY: Assumes uid_t and gid_t are of same size (shouldn't be a problem)
230 if ((u
= getpwnam(optarg
)))
233 return GetNumericId((gid_t
*)&uid
, optarg
);
238 #endif //NO_USER_SWITCH
241 static __noreturn
void usage()
243 printerrorf("Incorrect parameters\n\n");
249 static __noreturn
void usage()
251 printerrorf("vlmcsd %s\n"
253 " %s [ options ]\n\n"
256 " -w <ePID> always use <ePID> for Windows\n"
257 " -0 <ePID> always use <ePID> for Office2010\n"
258 " -3 <ePID> always use <ePID> for Office2013\n"
259 " -H <HwId> always use hardware Id <HwId>\n"
261 #if !defined(_WIN32) && !defined(NO_USER_SWITCH)
262 " -u <user> set uid to <user>\n"
263 " -g <group> set gid to <group>\n"
264 #endif // !defined(_WIN32) && !defined(NO_USER_SWITCH)
265 #ifndef NO_RANDOM_EPID
266 " -r 0|1|2\t\tset ePID randomization level (default 1)\n"
267 " -C <LCID>\t\tuse fixed <LCID> in random ePIDs\n"
268 #endif // NO_RANDOM_EPID
271 " -4\t\t\tuse IPv4\n"
272 " -6\t\t\tuse IPv6\n"
273 " -L <address>[:<port>]\tlisten on IP address <address> with optional <port>\n"
274 " -P <port>\t\tset TCP port <port> for subsequent -L statements (default 1688)\n"
276 " -P <port>\t\tuse TCP port <port> (default 1688)\n"
278 #if !defined(NO_LIMIT) && !__minix__
279 " -m <clients>\t\tHandle max. <clients> simultaneously (default no limit)\n"
280 #endif // !defined(NO_LIMIT) && !__minix__
282 " -s install vlmcsd as an NT service. Ignores -e"
287 " -S remove vlmcsd service. Ignores all other options\n"
288 " -U <username> run NT service as <username>. Must be used with -s\n"
289 " -W <password> optional <password> for -U. Must be used with -s\n"
292 " -e log to stdout\n"
295 " -D run in foreground\n"
296 " -f run in foreground"
304 #if !defined(NO_TIMEOUT) && !__minix__
305 " -t <seconds>\t\tdisconnect clients after <seconds> of inactivity (default 30)\n"
306 #endif // !defined(NO_TIMEOUT) && !__minix__
307 " -d\t\t\tdisconnect clients after each request\n"
308 " -k\t\t\tdon't disconnect clients after each request (default)\n"
309 " -N0, -N1\t\tdisable/enable NDR64\n"
310 " -B0, -B1\t\tdisable/enable bind time feature negotiation\n"
313 " -p <file> write pid to <file>\n"
314 #endif // NO_PID_FILE
316 " -i <file>\t\tuse config file <file>\n"
317 #endif // NO_INI_FILE
318 #ifndef NO_CUSTOM_INTERVALS
319 " -R <interval> renew activation every <interval> (default 1w)\n"
320 " -A <interval> retry activation every <interval> (default 2h)\n"
321 #endif // NO_CUSTOM_INTERVALS
324 " -l syslog log to syslog\n"
326 " -l <file> log to <file>\n"
327 #ifndef NO_VERBOSE_LOG
328 " -v\t\t\tlog verbose\n"
329 " -q\t\t\tdon't log verbose (default)\n"
330 #endif // NO_VERBOSE_LOG
332 " -V display version information and exit"
334 Version
, global_argv
[0]);
341 #ifndef NO_CUSTOM_INTERVALS
343 // Convert time span strings (e.g. "2h", "5w") to minutes
344 __pure
static DWORD
timeSpanString2Minutes(const char *const restrict argument
)
348 long long val
= strtoll(argument
, &unitId
, 10);
350 switch(toupper((int)*unitId
))
371 if (val
< 1) val
= 1;
372 if (val
> UINT_MAX
) val
= UINT_MAX
;
379 __pure
static BOOL
getTimeSpanFromIniFile(DWORD
* result
, const char *const restrict argument
)
381 DWORD val
= timeSpanString2Minutes(argument
);
384 IniFileErrorMessage
= "Incorrect time span.";
391 #endif // NO_INI_FILE
394 __pure
static DWORD
getTimeSpanFromCommandLine(const char *const restrict optarg
, const char optchar
)
396 long long val
= timeSpanString2Minutes(optarg
);
400 printerrorf("Fatal: No valid time span specified in option -%c.\n", optchar
);
407 #endif // NO_CUSTOM_INTERVALS
411 static void ignoreIniFileParameter(uint_fast8_t iniFileParameterId
)
415 for (i
= 0; i
< _countof(IniFileParameterList
); i
++)
417 if (IniFileParameterList
[i
].Id
!= iniFileParameterId
) continue;
418 IniFileParameterList
[i
].Id
= 0;
423 #define ignoreIniFileParameter(x)
424 #endif // NO_INI_FILE
428 static BOOL
getIniFileArgumentBool(int_fast8_t *result
, const char *const argument
)
430 IniFileErrorMessage
= "Argument must be true/on/yes/1 or false/off/no/0";
431 return getArgumentBool(result
, argument
);
435 static BOOL
getIniFileArgumentInt(unsigned int *result
, const char *const argument
, const unsigned int min
, const unsigned int max
)
437 unsigned int tempResult
;
439 if (!stringToInt(argument
, min
, max
, &tempResult
))
441 snprintf(IniFileErrorBuffer
, INIFILE_ERROR_BUFFERSIZE
, "Must be integer between %u and %u", min
, max
);
442 IniFileErrorMessage
= IniFileErrorBuffer
;
446 *result
= tempResult
;
451 static char* allocateStringArgument(const char *const argument
)
453 char* result
= (char*)vlmcsd_malloc(strlen(argument
) + 1);
454 strcpy(result
, argument
);
459 static BOOL
setIniFileParameter(uint_fast8_t id
, const char *const iniarg
)
466 # if !defined(NO_USER_SWITCH) && !defined(_WIN32)
471 IniFileErrorMessage
= "Invalid group id or name";
472 if (!(gname
= allocateStringArgument(iniarg
))) return FALSE
;
474 if ((g
= getgrnam(iniarg
)))
477 success
= !GetNumericId(&gid
, iniarg
);
484 IniFileErrorMessage
= "Invalid user id or name";
485 if (!(uname
= allocateStringArgument(iniarg
))) return FALSE
;
487 if ((p
= getpwnam(iniarg
)))
490 success
= !GetNumericId(&uid
, iniarg
);
494 # endif // !defined(NO_USER_SWITCH) && !defined(_WIN32)
496 # ifndef NO_RANDOM_EPID
499 success
= getIniFileArgumentInt(&result
, iniarg
, 0, 32767);
500 if (success
) Lcid
= (uint16_t)result
;
503 case INI_PARAM_RANDOMIZATION_LEVEL
:
504 success
= getIniFileArgumentInt(&result
, iniarg
, 0, 2);
505 if (success
) RandomizationLevel
= (int_fast8_t)result
;
508 # endif // NO_RANDOM_EPID
513 defaultport
= allocateStringArgument(iniarg
);
518 # if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
520 case INI_PARAM_LISTEN
:
524 # if !defined(NO_LIMIT) && !__minix__
526 case INI_PARAM_MAX_WORKERS
:
528 success
= getIniFileArgumentInt(&MaxTasks
, iniarg
, 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT
);
530 success
= getIniFileArgumentInt(&MaxTasks
, iniarg
, 1, SEM_VALUE_MAX
);
531 # endif // !USE_MSRPC
534 # endif // !defined(NO_LIMIT) && !__minix__
535 # endif // NO_SOCKETS
539 case INI_PARAM_PID_FILE
:
540 fn_pid
= allocateStringArgument(iniarg
);
543 # endif // NO_PID_FILE
547 case INI_PARAM_LOG_FILE
:
548 fn_log
= allocateStringArgument(iniarg
);
551 # ifndef NO_VERBOSE_LOG
552 case INI_PARAM_LOG_VERBOSE
:
553 success
= getIniFileArgumentBool(&logverbose
, iniarg
);
556 # endif // NO_VERBOSE_LOG
559 # ifndef NO_CUSTOM_INTERVALS
561 case INI_PARAM_ACTIVATION_INTERVAL
:
562 success
= getTimeSpanFromIniFile(&VLActivationInterval
, iniarg
);
565 case INI_PARAM_RENEWAL_INTERVAL
:
566 success
= getTimeSpanFromIniFile(&VLRenewalInterval
, iniarg
);
569 # endif // NO_CUSTOM_INTERVALS
573 # if !defined(NO_TIMEOUT) && !__minix__
575 case INI_PARAM_CONNECTION_TIMEOUT
:
576 success
= getIniFileArgumentInt(&result
, iniarg
, 1, 600);
577 if (success
) ServerTimeout
= (DWORD
)result
;
580 # endif // !defined(NO_TIMEOUT) && !__minix__
582 case INI_PARAM_DISCONNECT_IMMEDIATELY
:
583 success
= getIniFileArgumentBool(&DisconnectImmediately
, iniarg
);
586 case INI_PARAM_RPC_NDR64
:
587 success
= getIniFileArgumentBool(&UseRpcNDR64
, iniarg
);
590 case INI_PARAM_RPC_BTFN
:
591 success
= getIniFileArgumentBool(&UseRpcBTFN
, iniarg
);
604 static __pure
int isControlCharOrSlash(const char c
)
606 if ((unsigned char)c
< '!') return !0;
607 if (c
== '/') return !0;
612 static void iniFileLineNextWord(const char **s
)
614 while ( **s
&& isspace((int)**s
) ) (*s
)++;
618 static BOOL
setHwIdFromIniFileLine(const char **s
, const ProdListIndex_t index
)
620 iniFileLineNextWord(s
);
624 if (KmsResponseParameters
[index
].HwId
) return TRUE
;
626 BYTE
* HwId
= (BYTE
*)vlmcsd_malloc(sizeof(((RESPONSE_V6
*)0)->HwId
));
627 hex2bin(HwId
, *s
+ 1, sizeof(((RESPONSE_V6
*)0)->HwId
));
628 KmsResponseParameters
[index
].HwId
= HwId
;
635 static BOOL
checkGuidInIniFileLine(const char **s
, ProdListIndex_t
*const index
)
639 if (!string2Uuid(*s
, &AppGuid
)) return FALSE
;
641 (*s
) += GUID_STRING_LENGTH
;
642 getProductNameHE(&AppGuid
, AppList
, index
);
644 if (*index
> getAppListSize() - 2)
646 IniFileErrorMessage
= "Unknown App Guid.";
650 iniFileLineNextWord(s
);
651 if ( *(*s
)++ != '=' ) return FALSE
;
657 static BOOL
setEpidFromIniFileLine(const char **s
, const ProdListIndex_t index
)
659 iniFileLineNextWord(s
);
660 const char *savedPosition
= *s
;
663 for (i
= 0; !isControlCharOrSlash(**s
); i
++)
665 if (utf8_to_ucs2_char((const unsigned char*)*s
, (const unsigned char**)s
) == (WCHAR
)~0)
671 if (i
< 1 || i
>= PID_BUFFER_SIZE
) return FALSE
;
672 if (KmsResponseParameters
[index
].Epid
) return TRUE
;
674 size_t size
= *s
- savedPosition
+ 1;
676 char* epidbuffer
= (char*)vlmcsd_malloc(size
);
677 memcpy(epidbuffer
, savedPosition
, size
- 1);
678 epidbuffer
[size
- 1] = 0;
680 KmsResponseParameters
[index
].Epid
= epidbuffer
;
683 KmsResponseParameters
[index
].EpidSource
= fn_ini
;
690 static BOOL
getIniFileArgument(const char **s
)
692 while (!isspace((int)**s
) && **s
!= '=' && **s
) (*s
)++;
693 iniFileLineNextWord(s
);
695 if (*((*s
)++) != '=')
697 IniFileErrorMessage
= "'=' required after keyword.";
701 iniFileLineNextWord(s
);
705 IniFileErrorMessage
= "missing argument after '='.";
713 static BOOL
handleIniFileParameter(const char *s
)
717 for (i
= 0; i
< _countof(IniFileParameterList
); i
++)
719 if (strncasecmp(IniFileParameterList
[i
].Name
, s
, strlen(IniFileParameterList
[i
].Name
))) continue;
720 if (!IniFileParameterList
[i
].Id
) return TRUE
;
722 if (!getIniFileArgument(&s
)) return FALSE
;
724 return setIniFileParameter(IniFileParameterList
[i
].Id
, s
);
727 IniFileErrorMessage
= "Unknown keyword.";
732 #if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
733 static BOOL
setupListeningSocketsFromIniFile(const char *s
)
735 if (!maxsockets
) return TRUE
;
736 if (strncasecmp("Listen", s
, 6)) return TRUE
;
737 if (!getIniFileArgument(&s
)) return TRUE
;
739 snprintf(IniFileErrorBuffer
, INIFILE_ERROR_BUFFERSIZE
, "Cannot listen on %s.", s
);
740 IniFileErrorMessage
= IniFileErrorBuffer
;
741 return addListeningSocket(s
);
743 #endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC)
746 static BOOL
readIniFile(const uint_fast8_t pass
)
750 ProdListIndex_t appIndex
;
751 unsigned int lineNumber
;
752 uint_fast8_t lineParseError
;
757 IniFileErrorBuffer
= (char*)vlmcsd_malloc(INIFILE_ERROR_BUFFERSIZE
);
759 if ( !(f
= fopen(fn_ini
, "r") )) return FALSE
;
761 for (lineNumber
= 1; (s
= fgets(line
, sizeof(line
), f
)); lineNumber
++)
763 line
[strlen(line
) - 1] = 0;
765 iniFileLineNextWord(&s
);
766 if (*s
== ';' || *s
== '#' || !*s
) continue;
769 if (pass
== INI_FILE_PASS_1
)
770 # endif // NO_SOCKETS
772 if (handleIniFileParameter(s
)) continue;
774 lineParseError
= !checkGuidInIniFileLine(&s
, &appIndex
) ||
775 !setEpidFromIniFileLine(&s
, appIndex
) ||
776 !setHwIdFromIniFileLine(&s
, appIndex
);
778 # if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
779 else if (pass
== INI_FILE_PASS_2
)
781 lineParseError
= !setupListeningSocketsFromIniFile(s
);
787 # endif // NO_SOCKETS
791 printerrorf("Warning: %s line %u: \"%s\". %s\n", fn_ini
, lineNumber
, line
, IniFileErrorMessage
);
796 if (ferror(f
)) result
= FALSE
;
798 free(IniFileErrorBuffer
);
801 # if !defined(NO_SOCKETS) && !defined(NO_LOG)
803 if (pass
== INI_FILE_PASS_1
&& !InetdMode
&& result
)
807 # endif // _NTSERVICE
808 logger("Read ini file %s\n", fn_ini
);
811 # endif // !defined(NO_SOCKETS) && !defined(NO_LOG)
815 #endif // NO_INI_FILE
818 #if !defined(NO_SOCKETS)
820 #if !defined(NO_SIGHUP)
821 static void exec_self(char** argv
)
823 # if __linux__ && defined(USE_AUXV)
825 char *execname_ptr
= (char*)getauxval(AT_EXECFN
);
826 if (execname_ptr
) execv(execname_ptr
, argv
);
828 # elif (__linux__ || __CYGWIN__) && !defined(NO_PROCFS)
830 execv(realpath("/proc/self/exe", NULL
), argv
);
832 # elif (__FreeBSD__) && !defined(NO_PROCFS)
837 mib
[2] = KERN_PROC_PATHNAME
;
839 char path
[PATH_MAX
+ 1];
840 size_t cb
= sizeof(path
);
841 if (!sysctl(mib
, 4, path
, &cb
, NULL
, 0)) execv(path
, argv
);
843 # elif (__DragonFly__) && !defined(NO_PROCFS)
845 execv(realpath("/proc/curproc/file", NULL
), argv
);
847 # elif __NetBSD__ && !defined(NO_PROCFS)
849 execv(realpath("/proc/curproc/exe", NULL
), argv
);
853 const char* exename
= getexecname();
854 if (exename
) execv(exename
, argv
);
858 char path
[PATH_MAX
+ 1];
859 uint32_t size
= sizeof(path
);
860 if (_NSGetExecutablePath(path
, &size
) == 0) execv(path
, argv
);
864 execvp(argv
[0], argv
);
870 static void HangupHandler(const int signal_unused
)
873 int_fast8_t daemonize_protection
= TRUE
;
874 CARGV argv_in
= multi_argv
== NULL
? global_argv
: multi_argv
;
875 int argc_in
= multi_argc
== 0 ? global_argc
: multi_argc
;
876 const char** argv_out
= (const char**)vlmcsd_malloc((argc_in
+ 2) * sizeof(char**));
878 for (i
= 0; i
< argc_in
; i
++)
880 if (!strcmp(argv_in
[i
], "-Z")) daemonize_protection
= FALSE
;
881 argv_out
[i
] = argv_in
[i
];
884 argv_out
[argc_in
] = argv_out
[argc_in
+ 1] = NULL
;
885 if (daemonize_protection
) argv_out
[argc_in
] = (char*) "-Z";
887 exec_self((char**)argv_out
);
890 logger("Fatal: Unable to restart on SIGHUP: %s\n", strerror(errno
));
894 if (fn_pid
) unlink(fn_pid
);
895 # endif // NO_PID_FILE
901 static void terminationHandler(const int signal_unused
)
908 #if defined(CHILD_HANDLER) || __minix__
909 static void childHandler(const int signal
)
911 waitpid(-1, NULL
, WNOHANG
);
913 #endif // defined(CHILD_HANDLER) || __minix__
916 static int daemonizeAndSetSignalAction()
919 sigemptyset(&sa
.sa_mask
);
922 if ( !nodaemon
) if (daemon(!0, logstdout
))
924 if ( !nodaemon
) if (daemon(!0, 0))
927 printerrorf("Fatal: Could not daemonize to background.\n");
935 # if defined(CHILD_HANDLER) || __minix__
936 sa
.sa_handler
= childHandler
;
937 # else // !(defined(CHILD_HANDLER) || __minix__)
938 sa
.sa_handler
= SIG_IGN
;
939 # endif // !(defined(CHILD_HANDLER) || __minix__)
940 sa
.sa_flags
= SA_NOCLDWAIT
;
942 if (sigaction(SIGCHLD
, &sa
, NULL
))
945 # endif // !USE_THREADS
947 sa
.sa_handler
= terminationHandler
;
950 sigaction(SIGINT
, &sa
, NULL
);
951 sigaction(SIGTERM
, &sa
, NULL
);
954 sa
.sa_handler
= HangupHandler
;
955 sa
.sa_flags
= SA_NODEFER
;
956 sigaction(SIGHUP
, &sa
, NULL
);
966 static BOOL
terminationHandler(const DWORD fdwCtrlType
)
968 // What a lame substitute for Unix signal handling
972 case CTRL_CLOSE_EVENT
:
973 case CTRL_BREAK_EVENT
:
974 case CTRL_LOGOFF_EVENT
:
975 case CTRL_SHUTDOWN_EVENT
:
984 static DWORD
daemonizeAndSetSignalAction()
986 if(!SetConsoleCtrlHandler( (PHANDLER_ROUTINE
) terminationHandler
, TRUE
))
989 DWORD rc
= GetLastError();
990 logger("Warning: Could not register Windows signal handler: Error %u\n", rc
);
994 return ERROR_SUCCESS
;
997 #endif // !defined(NO_SOCKETS)
1000 // Workaround for Cygwin fork bug (only affects cygwin processes that are Windows services)
1001 // Best is to compile for Cygwin with threads. fork() is slow and unreliable on Cygwin
1002 #if !defined(NO_INI_FILE) || !defined(NO_LOG) || !defined(NO_CL_PIDS)
1003 __pure
static char* getCommandLineArg(char *const restrict optarg
)
1005 #if !defined (__CYGWIN__) || defined(USE_THREADS) || defined(NO_SOCKETS)
1008 if (!IsNTService
) return optarg
;
1010 return allocateStringArgument(optarg
);
1013 #endif // !defined(NO_INI_FILE) || !defined(NO_LOG) || !defined(NO_CL_PIDS)
1016 static void parseGeneralArguments() {
1021 #endif // NO_CL_PIDS
1023 for (opterr
= 0; ( o
= getopt(global_argc
, (char* const*)global_argv
, optstring
) ) > 0; ) switch (o
)
1025 #if !defined(NO_SOCKETS) && !defined(NO_SIGHUP) && !defined(_WIN32)
1030 #endif // !defined(NO_SOCKETS) && !defined(NO_SIGHUP) && !defined(_WIN32)
1034 KmsResponseParameters
[APP_ID_WINDOWS
].Epid
= getCommandLineArg(optarg
);
1036 KmsResponseParameters
[APP_ID_WINDOWS
].EpidSource
= "command line";
1041 KmsResponseParameters
[APP_ID_OFFICE2010
].Epid
= getCommandLineArg(optarg
);
1043 KmsResponseParameters
[APP_ID_OFFICE2010
].EpidSource
= "command line";
1048 KmsResponseParameters
[APP_ID_OFFICE2013
].Epid
= getCommandLineArg(optarg
);
1050 KmsResponseParameters
[APP_ID_OFFICE2013
].EpidSource
= "command line";
1055 HwId
= (BYTE
*)vlmcsd_malloc(sizeof(((RESPONSE_V6
*)0)->HwId
));
1057 hex2bin(HwId
, optarg
, sizeof(((RESPONSE_V6
*)0)->HwId
));
1059 KmsResponseParameters
[APP_ID_WINDOWS
].HwId
= HwId
;
1060 KmsResponseParameters
[APP_ID_OFFICE2010
].HwId
= HwId
;
1061 KmsResponseParameters
[APP_ID_OFFICE2013
].HwId
= HwId
;
1063 #endif // NO_CL_PIDS
1071 ignoreIniFileParameter(INI_PARAM_LISTEN
);
1075 defaultport
= optarg
;
1076 ignoreIniFileParameter(INI_PARAM_PORT
);
1080 #if !defined(NO_LIMIT) && !__minix__
1084 MaxTasks
= getOptionArgumentInt(o
, 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT
);
1086 MaxTasks
= getOptionArgumentInt(o
, 1, SEM_VALUE_MAX
);
1087 #endif // !USE_MSRPC
1088 ignoreIniFileParameter(INI_PARAM_MAX_WORKERS
);
1091 #endif // !defined(NO_LIMIT) && !__minix__
1092 #endif // NO_SOCKETS
1094 #if !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC)
1096 ServerTimeout
= getOptionArgumentInt(o
, 1, 600);
1097 ignoreIniFileParameter(INI_PARAM_CONNECTION_TIMEOUT
);
1099 #endif // !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC)
1103 fn_pid
= getCommandLineArg(optarg
);
1104 ignoreIniFileParameter(INI_PARAM_PID_FILE
);
1110 fn_ini
= getCommandLineArg(optarg
);
1111 if (!strcmp(fn_ini
, "-")) fn_ini
= NULL
;
1117 fn_log
= getCommandLineArg(optarg
);
1118 ignoreIniFileParameter(INI_PARAM_LOG_FILE
);
1121 #ifndef NO_VERBOSE_LOG
1124 logverbose
= o
== 'v';
1125 ignoreIniFileParameter(INI_PARAM_LOG_VERBOSE
);
1128 #endif // NO_VERBOSE_LOG
1135 ignoreIniFileParameter(INI_PARAM_LISTEN
);
1148 ServiceUser
= optarg
;
1152 ServicePassword
= optarg
;
1157 if (InetdMode
) usage();
1159 if (!IsNTService
) installService
= 1; // Install
1163 if (!IsNTService
) installService
= 2; // Remove
1165 #endif // _NTSERVICE
1176 #endif // NO_SOCKETS
1179 case 'I': // Backward compatibility with svn681 and earlier
1183 #ifndef NO_RANDOM_EPID
1185 RandomizationLevel
= (int_fast8_t)getOptionArgumentInt(o
, 0, 2);
1186 ignoreIniFileParameter(INI_PARAM_RANDOMIZATION_LEVEL
);
1190 Lcid
= (uint16_t)getOptionArgumentInt(o
, 0, 32767);
1192 ignoreIniFileParameter(INI_PARAM_LCID
);
1195 if (!IsValidLcid(Lcid
))
1197 printerrorf("Warning: %s is not a valid LCID.\n", optarg
);
1202 #endif // NO_RANDOM_PID
1204 #if !defined(NO_USER_SWITCH) && !defined(_WIN32)
1207 ignoreIniFileParameter(INI_PARAM_GID
);
1213 printerrorf("Fatal: setgid for %s failed.\n", optarg
);
1220 ignoreIniFileParameter(INI_PARAM_UID
);
1226 printerrorf("Fatal: setuid for %s failed.\n", optarg
);
1230 #endif // NO_USER_SWITCH && !_WIN32
1232 #ifndef NO_CUSTOM_INTERVALS
1234 VLRenewalInterval
= getTimeSpanFromCommandLine(optarg
, o
);
1235 ignoreIniFileParameter(INI_PARAM_RENEWAL_INTERVAL
);
1239 VLActivationInterval
= getTimeSpanFromCommandLine(optarg
, o
);
1240 ignoreIniFileParameter(INI_PARAM_ACTIVATION_INTERVAL
);
1247 DisconnectImmediately
= o
== 'd';
1248 ignoreIniFileParameter(INI_PARAM_DISCONNECT_IMMEDIATELY
);
1252 if (!getArgumentBool(&UseRpcNDR64
, optarg
)) usage();
1253 ignoreIniFileParameter(INI_PARAM_RPC_NDR64
);
1257 if (!getArgumentBool(&UseRpcBTFN
, optarg
)) usage();
1258 ignoreIniFileParameter(INI_PARAM_RPC_BTFN
);
1260 #endif // !USE_MSRPC
1264 if (IsNTService
) break;
1266 printf("vlmcsd %s\n", Version
);
1273 // Do not allow non-option arguments
1274 if (optind
!= global_argc
)
1278 // -U and -W must be used with -s
1279 if ((ServiceUser
|| *ServicePassword
) && installService
!= 1) usage();
1280 #endif // _NTSERVICE
1285 static void writePidFile()
1288 if (IsRestarted
) return;
1289 # endif // NO_SIGHUP
1291 if (fn_pid
&& !InetdMode
)
1293 FILE *_f
= fopen(fn_pid
, "w");
1297 fprintf(_f
, "%u", (uint32_t)getpid());
1304 logger("Warning: Cannot write pid file '%s'. %s.\n", fn_pid
, strerror(errno
));
1310 #define writePidFile(x)
1311 #endif // NO_PID_FILE
1313 #if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1321 if (fn_pid
) unlink(fn_pid
);
1322 #endif // NO_PID_FILE
1323 closeAllListeningSockets();
1325 #if !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !defined(_WIN32) && !__minix__
1326 sem_unlink("/vlmcsd");
1327 #if !defined(USE_THREADS) && !defined(CYGWIN)
1330 if (Semaphore
!= (sem_t
*)-1) shmdt(Semaphore
);
1331 shmctl(shmid
, IPC_RMID
, NULL
);
1333 #endif // !defined(USE_THREADS) && !defined(CYGWIN)
1334 #endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !defined(_WIN32) && !__minix__
1337 logger("vlmcsd %s was shutdown\n", Version
);
1343 #elif defined(USE_MSRPC)
1347 # ifndef NO_PID_FILE
1348 if (fn_pid
) unlink(fn_pid
);
1349 # endif // NO_PID_FILE
1352 logger("vlmcsd %s was shutdown\n", Version
);
1356 #else // Neither Sockets nor RPC
1358 __pure
void cleanup() {}
1360 #endif // Neither Sockets nor RPC
1363 #if !defined(USE_MSRPC) && !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__
1364 // Get a semaphore for limiting the maximum concurrent tasks
1365 static void allocateSemaphore(void)
1374 sem_unlink("/vlmcsd");
1377 if(MaxTasks
< SEM_VALUE_MAX
&& !InetdMode
)
1381 #if !defined(USE_THREADS) && !defined(CYGWIN)
1383 if ((Semaphore
= sem_open("/vlmcsd", O_CREAT
/*| O_EXCL*/, 0700, MaxTasks
)) == SEM_FAILED
) // fails on many systems
1385 // We didn't get a named Semaphore (/dev/shm on Linux) so let's try our own shared page
1388 ( shmid
= shmget(IPC_PRIVATE
, sizeof(sem_t
), IPC_CREAT
| 0600) ) < 0 ||
1389 ( Semaphore
= (sem_t
*)shmat(shmid
, NULL
, 0) ) == (sem_t
*)-1 ||
1390 sem_init(Semaphore
, 1, MaxTasks
) < 0
1393 int errno_save
= errno
;
1394 if (Semaphore
!= (sem_t
*)-1) shmdt(Semaphore
);
1395 if (shmid
>= 0) shmctl(shmid
, IPC_RMID
, NULL
);
1396 printerrorf("Warning: Could not create semaphore: %s\n", vlmcsd_strerror(errno_save
));
1397 MaxTasks
= SEM_VALUE_MAX
;
1401 #else // THREADS or CYGWIN
1403 Semaphore
= (sem_t
*)vlmcsd_malloc(sizeof(sem_t
));
1405 if (sem_init(Semaphore
, sharemode
, MaxTasks
) < 0) // sem_init is not implemented on Darwin (returns ENOSYS)
1409 if ((Semaphore
= sem_open("/vlmcsd", O_CREAT
/*| O_EXCL*/, 0700, MaxTasks
)) == SEM_FAILED
)
1411 printerrorf("Warning: Could not create semaphore: %s\n", vlmcsd_strerror(errno
));
1412 MaxTasks
= SEM_VALUE_MAX
;
1416 #endif // THREADS or CYGWIN
1420 if (!(Semaphore
= CreateSemaphoreA(NULL
, MaxTasks
, MaxTasks
, NULL
)))
1422 printerrorf("Warning: Could not create semaphore: %s\n", vlmcsd_strerror(GetLastError()));
1423 MaxTasks
= SEM_VALUE_MAX
;
1429 #endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__
1432 #if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1433 int setupListeningSockets()
1436 uint_fast8_t allocsockets
= maxsockets
? maxsockets
: 2;
1438 SocketList
= (SOCKET
*)vlmcsd_malloc((size_t)allocsockets
* sizeof(SOCKET
));
1440 haveIPv4Stack
= checkProtocolStack(AF_INET
);
1441 haveIPv6Stack
= checkProtocolStack(AF_INET6
);
1443 // Reset getopt since we've alread used it
1446 for (opterr
= 0; ( o
= getopt(global_argc
, (char* const*)global_argv
, optstring
) ) > 0; ) switch (o
)
1452 printerrorf("Fatal: Your system does not support %s.\n", cIPv4
);
1462 printerrorf("Fatal: Your system does not support %s.\n", cIPv6
);
1470 addListeningSocket(optarg
);
1475 defaultport
= optarg
;
1484 # ifndef NO_INI_FILE
1485 if (maxsockets
&& !numsockets
)
1487 if (fn_ini
&& !readIniFile(INI_FILE_PASS_2
))
1490 if (strcmp(fn_ini
, INI_FILE
))
1492 printerrorf("Warning: Can't read %s: %s\n", fn_ini
, strerror(errno
));
1497 // if -L hasn't been specified on the command line, use default sockets (all IP addresses)
1498 // maxsocket results from first pass parsing the arguments
1501 if (haveIPv6Stack
&& (v6required
|| !v4required
)) addListeningSocket("::");
1502 if (haveIPv4Stack
&& (v4required
|| !v6required
)) addListeningSocket("0.0.0.0");
1507 printerrorf("Fatal: Could not listen on any socket.\n");
1513 #endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1516 int server_main(int argc
, CARGV argv
)
1518 #if !defined(_NTSERVICE) && !defined(NO_SOCKETS)
1520 #endif // !defined(_NTSERVICE) && !defined(NO_SOCKETS)
1522 // Initialize ePID / HwId parameters
1523 memset(KmsResponseParameters
, 0, sizeof(KmsResponseParameters
));
1528 #ifdef _NTSERVICE // #endif is in newmain()
1529 DWORD lasterror
= ERROR_SUCCESS
;
1531 if (!StartServiceCtrlDispatcher(NTServiceDispatchTable
) && (lasterror
= GetLastError()) == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
)
1533 IsNTService
= FALSE
;
1545 // Initialize thread synchronization objects for Windows and Cygwin
1549 // Initialize the Critical Section for proper logging
1550 InitializeCriticalSection(&logmutex
);
1553 #endif // USE_THREADS
1558 // Windows Sockets must be initialized
1561 if ((error
= WSAStartup(0x0202, &wsadata
)))
1563 printerrorf("Fatal: Could not initialize Windows sockets (Error: %d).\n", error
);
1568 // Windows can never daemonize
1573 // Do not daemonize if we are a Windows service
1574 if (IsNTService
) nodaemon
= 1;
1576 #endif // _WIN32 / __CYGWIN__
1577 #endif // _NTSERVICE ( #ifdef is main(int argc, CARGV argv) )
1579 parseGeneralArguments(); // Does not return if an error occurs
1581 #if !defined(_WIN32) && !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1582 struct stat statbuf
;
1583 fstat(STDIN_FILENO
, &statbuf
);
1584 if (S_ISSOCK(statbuf
.st_mode
))
1593 #endif // !defined(_WIN32) && !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1596 if (fn_ini
&& !readIniFile(INI_FILE_PASS_1
))
1599 if (strcmp(fn_ini
, INI_FILE
))
1601 printerrorf("Warning: Can't read %s: %s\n", fn_ini
, strerror(errno
));
1603 #endif // NO_INI_FILE
1605 #if !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__ && !defined(USE_MSRPC)
1606 allocateSemaphore();
1607 #endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && __minix__
1611 return NtServiceInstallation(installService
, ServiceUser
, ServicePassword
);
1612 #endif // _NTSERVICE
1614 #if !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1617 if ((error
= setupListeningSockets())) return error
;
1619 #endif // NO_SOCKETS
1621 // After sockets have been set up, we may switch to a lower privileged user
1622 #if !defined(_WIN32) && !defined(NO_USER_SWITCH)
1628 if (gid
!= INVALID_GID
&& setgid(gid
))
1630 printerrorf("Fatal: setgid for %s failed.\n", gname
);
1634 if (uid
!= INVALID_UID
&& setuid(uid
))
1636 printerrorf("Fatal: setuid for %s failed.\n", uname
);
1643 #endif // !defined(_WIN32) && !defined(NO_USER_SWITCH)
1647 // Randomization Level 1 means generate ePIDs at startup and use them during
1648 // the lifetime of the process. So we generate them now
1649 #ifndef NO_RANDOM_EPID
1650 if (RandomizationLevel
== 1) randomPidInit();
1653 #if !defined(NO_SOCKETS)
1657 if ((error
= daemonizeAndSetSignalAction())) return error
;
1658 #endif // !defined(NO_SOCKETS)
1662 #if !defined(NO_LOG) && !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1664 logger("vlmcsd %s started successfully\n", Version
);
1665 #endif // !defined(NO_LOG) && !defined(NO_SOCKETS) && !defined(USE_MSRPC)
1667 #if defined(_NTSERVICE) && !defined(USE_MSRPC)
1668 if (IsNTService
) ReportServiceStatus(SERVICE_RUNNING
, NO_ERROR
, 200);
1669 #endif // defined(_NTSERVICE) && !defined(USE_MSRPC)
1674 // Clean up things and exit
1676 if (!ServiceShutdown
)
1681 ReportServiceStatus(SERVICE_STOPPED
, NO_ERROR
, 0);