3 extern unsigned long tls_index;
\r
4 extern bool is_admin;
\r
5 extern imports_t imports;
\r
7 static TCHAR unquoted_imagepath[PATH_LENGTH];
\r
8 static TCHAR imagepath[PATH_LENGTH];
\r
9 static TCHAR imageargv0[PATH_LENGTH];
\r
11 /* Are two strings case-insensitively equivalent? */
\r
12 int str_equiv(const TCHAR *a, const TCHAR *b) {
\r
13 size_t len = _tcslen(a);
\r
14 if (_tcslen(b) != len) return 0;
\r
15 if (_tcsnicmp(a, b, len)) return 0;
\r
19 /* Convert a string to a number. */
\r
20 int str_number(const TCHAR *string, unsigned long *number, TCHAR **bogus) {
\r
21 if (! string) return 1;
\r
23 *number = _tcstoul(string, bogus, 0);
\r
24 if (**bogus) return 2;
\r
29 int str_number(const TCHAR *string, unsigned long *number) {
\r
31 return str_number(string, number, &bogus);
\r
34 /* Does a char need to be escaped? */
\r
35 static bool needs_escape(const TCHAR c) {
\r
36 if (c == _T('"')) return true;
\r
37 if (c == _T('&')) return true;
\r
38 if (c == _T('%')) return true;
\r
39 if (c == _T('^')) return true;
\r
40 if (c == _T('<')) return true;
\r
41 if (c == _T('>')) return true;
\r
42 if (c == _T('|')) return true;
\r
46 /* Does a char need to be quoted? */
\r
47 static bool needs_quote(const TCHAR c) {
\r
48 if (c == _T(' ')) return true;
\r
49 if (c == _T('\t')) return true;
\r
50 if (c == _T('\n')) return true;
\r
51 if (c == _T('\v')) return true;
\r
52 if (c == _T('"')) return true;
\r
53 if (c == _T('*')) return true;
\r
54 return needs_escape(c);
\r
57 /* https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ */
\r
58 /* http://www.robvanderwoude.com/escapechars.php */
\r
59 int quote(const TCHAR *unquoted, TCHAR *buffer, size_t buflen) {
\r
61 size_t len = _tcslen(unquoted);
\r
62 if (len > buflen - 1) return 1;
\r
64 bool escape = false;
\r
65 bool quotes = false;
\r
67 for (i = 0; i < len; i++) {
\r
68 if (needs_escape(unquoted[i])) {
\r
69 escape = quotes = true;
\r
72 if (needs_quote(unquoted[i])) quotes = true;
\r
75 memmove(buffer, unquoted, (len + 1) * sizeof(TCHAR));
\r
80 size_t quoted_len = 2;
\r
81 if (escape) quoted_len += 2;
\r
82 for (i = 0; ; i++) {
\r
85 while (i != len && unquoted[i] == _T('\\')) {
\r
91 quoted_len += n * 2;
\r
94 else if (unquoted[i] == _T('"')) quoted_len += n * 2 + 2;
\r
95 else quoted_len += n + 1;
\r
96 if (needs_escape(unquoted[i])) quoted_len += n;
\r
98 if (quoted_len > buflen - 1) return 1;
\r
101 if (escape) *s++ = _T('^');
\r
104 for (i = 0; ; i++) {
\r
107 while (i != len && unquoted[i] == _T('\\')) {
\r
113 for (j = 0; j < n * 2; j++) {
\r
114 if (escape) *s++ = _T('^');
\r
119 else if (unquoted[i] == _T('"')) {
\r
120 for (j = 0; j < n * 2 + 1; j++) {
\r
121 if (escape) *s++ = _T('^');
\r
124 if (escape && needs_escape(unquoted[i])) *s++ = _T('^');
\r
125 *s++ = unquoted[i];
\r
128 for (j = 0; j < n; j++) {
\r
129 if (escape) *s++ = _T('^');
\r
132 if (escape && needs_escape(unquoted[i])) *s++ = _T('^');
\r
133 *s++ = unquoted[i];
\r
136 if (escape) *s++ = _T('^');
\r
143 /* Remove basename of a path. */
\r
144 void strip_basename(TCHAR *buffer) {
\r
145 size_t len = _tcslen(buffer);
\r
147 for (i = len; i && buffer[i] != _T('\\') && buffer[i] != _T('/'); i--);
\r
149 if (i && buffer[i - 1] == _T(':')) i++;
\r
150 buffer[i] = _T('\0');
\r
153 /* How to use me correctly */
\r
154 int usage(int ret) {
\r
155 if (GetConsoleWindow()) print_message(stderr, NSSM_MESSAGE_USAGE, NSSM_VERSION, NSSM_CONFIGURATION, NSSM_DATE);
\r
156 else popup_message(0, MB_OK, NSSM_MESSAGE_USAGE, NSSM_VERSION, NSSM_CONFIGURATION, NSSM_DATE);
\r
160 void check_admin() {
\r
163 /* Lifted from MSDN examples */
\r
164 PSID AdministratorsGroup;
\r
165 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
\r
166 if (! AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup)) return;
\r
167 CheckTokenMembership(0, AdministratorsGroup, /*XXX*/(PBOOL) &is_admin);
\r
168 FreeSid(AdministratorsGroup);
\r
171 static int elevate(int argc, TCHAR **argv, unsigned long message) {
\r
172 print_message(stderr, message);
\r
174 SHELLEXECUTEINFO sei;
\r
175 ZeroMemory(&sei, sizeof(sei));
\r
176 sei.cbSize = sizeof(sei);
\r
177 sei.lpVerb = _T("runas");
\r
178 sei.lpFile = (TCHAR *) nssm_imagepath();
\r
180 TCHAR *args = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, EXE_LENGTH * sizeof(TCHAR));
\r
182 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("GetCommandLine()"), _T("elevate()"));
\r
186 /* Get command line, which includes the path to NSSM, and skip that part. */
\r
187 _sntprintf_s(args, EXE_LENGTH, _TRUNCATE, _T("%s"), GetCommandLine());
\r
188 size_t s = _tcslen(argv[0]) + 1;
\r
189 if (args[0] == _T('"')) s += 2;
\r
190 while (isspace(args[s])) s++;
\r
192 sei.lpParameters = args + s;
\r
193 sei.nShow = SW_SHOW;
\r
195 unsigned long exitcode = 0;
\r
196 if (! ShellExecuteEx(&sei)) exitcode = 100;
\r
198 HeapFree(GetProcessHeap(), 0, (void *) args);
\r
203 DWORD_PTR i, affinity, system_affinity;
\r
204 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) return 64;
\r
205 for (i = 0; system_affinity & (1LL << i); i++);
\r
209 const TCHAR *nssm_unquoted_imagepath() {
\r
210 return unquoted_imagepath;
\r
213 const TCHAR *nssm_imagepath() {
\r
217 const TCHAR *nssm_exe() {
\r
221 int _tmain(int argc, TCHAR **argv) {
\r
226 Ensure we write in UTF-16 mode, so that non-ASCII characters don't get
\r
227 mangled. If we were compiled in ANSI mode it won't work.
\r
229 _setmode(_fileno(stdout), _O_U16TEXT);
\r
230 _setmode(_fileno(stderr), _O_U16TEXT);
\r
233 /* Remember if we are admin */
\r
236 /* Set up function pointers. */
\r
237 if (get_imports()) exit(111);
\r
239 /* Remember our path for later. */
\r
240 _sntprintf_s(imageargv0, _countof(imageargv0), _TRUNCATE, _T("%s"), argv[0]);
\r
241 PathQuoteSpaces(imageargv0);
\r
242 GetModuleFileName(0, unquoted_imagepath, _countof(unquoted_imagepath));
\r
243 GetModuleFileName(0, imagepath, _countof(imagepath));
\r
244 PathQuoteSpaces(imagepath);
\r
249 Valid commands are:
\r
250 start, stop, pause, continue, install, edit, get, set, reset, unset, remove
\r
252 if (str_equiv(argv[1], _T("start"))) exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2));
\r
253 if (str_equiv(argv[1], _T("stop"))) exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2));
\r
254 if (str_equiv(argv[1], _T("restart"))) {
\r
255 int ret = control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2);
\r
256 if (ret) exit(ret);
\r
257 exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2));
\r
259 if (str_equiv(argv[1], _T("pause"))) exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2));
\r
260 if (str_equiv(argv[1], _T("continue"))) exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2));
\r
261 if (str_equiv(argv[1], _T("status"))) exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2));
\r
262 if (str_equiv(argv[1], _T("rotate"))) exit(control_service(NSSM_SERVICE_CONTROL_ROTATE, argc - 2, argv + 2));
\r
263 if (str_equiv(argv[1], _T("install"))) {
\r
264 if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL));
\r
266 exit(pre_install_service(argc - 2, argv + 2));
\r
268 if (str_equiv(argv[1], _T("edit")) || str_equiv(argv[1], _T("get")) || str_equiv(argv[1], _T("set")) || str_equiv(argv[1], _T("reset")) || str_equiv(argv[1], _T("unset"))) {
\r
269 int ret = pre_edit_service(argc - 1, argv + 1);
\r
270 if (ret == 3 && ! is_admin && argc == 3) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT));
\r
271 /* There might be a password here. */
\r
272 for (int i = 0; i < argc; i++) SecureZeroMemory(argv[i], _tcslen(argv[i]) * sizeof(TCHAR));
\r
275 if (str_equiv(argv[1], _T("list"))) exit(list_nssm_services());
\r
276 if (str_equiv(argv[1], _T("remove"))) {
\r
277 if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE));
\r
278 exit(pre_remove_service(argc - 2, argv + 2));
\r
282 /* Thread local storage for error message buffer */
\r
283 tls_index = TlsAlloc();
\r
285 /* Register messages */
\r
286 if (is_admin) create_messages();
\r
289 Optimisation for Windows 2000:
\r
290 When we're run from the command line the StartServiceCtrlDispatcher() call
\r
291 will time out after a few seconds on Windows 2000. On newer versions the
\r
292 call returns instantly. Check for stdin first and only try to call the
\r
293 function if there's no input stream found. Although it's possible that
\r
294 we're running with input redirected it's much more likely that we're
\r
295 actually running as a service.
\r
296 This will save time when running with no arguments from a command prompt.
\r
298 if (! GetStdHandle(STD_INPUT_HANDLE)) {
\r
299 /* Start service magic */
\r
300 SERVICE_TABLE_ENTRY table[] = { { NSSM, service_main }, { 0, 0 } };
\r
301 if (! StartServiceCtrlDispatcher(table)) {
\r
302 unsigned long error = GetLastError();
\r
303 /* User probably ran nssm with no argument */
\r
304 if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) exit(usage(1));
\r
305 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DISPATCHER_FAILED, error_string(error), 0);
\r
310 else exit(usage(1));
\r
312 /* And nothing more to do */
\r