1 // Copyright (c) 1999-2018 David Muse
2 // See the COPYING file for more information
3
4 #include <rudiments/process.h>
5 #include <rudiments/error.h>
6 #include <rudiments/snooze.h>
7 #include <rudiments/directory.h>
8 #include <rudiments/userentry.h>
9 #include <rudiments/groupentry.h>
10 #include <rudiments/file.h>
11 #include <rudiments/permissions.h>
12 #include <rudiments/charstring.h>
13 #include <rudiments/error.h>
14 #include <rudiments/stdio.h>
15 #ifdef RUDIMENTS_HAVE_CREATE_PROCESS
16 #include <rudiments/stringbuffer.h>
17 #include <rudiments/environment.h>
18 #include <rudiments/bytestring.h>
19 #endif
20 #if defined(RUDIMENTS_HAVE_GENERATECONSOLECTRLEVENT)
21 #include <rudiments/sys.h>
22 #endif
23 #ifdef RUDIMENTS_HAVE_EXECINFO_H
24 #include <execinfo.h>
25 #endif
26
27
28 #ifndef __USE_XOPEN_EXTENDED
29 // for getsid()
30 #define __USE_XOPEN_EXTENDED
31 #endif
32 #ifdef RUDIMENTS_HAVE_GRP_H
33 #include <grp.h>
34 #endif
35 #ifdef RUDIMENTS_HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 // for exit()
40 #ifdef RUDIMENTS_HAVE_STDLIB_H
41 #include <stdlib.h>
42 #endif
43
44 #ifdef RUDIMENTS_HAVE_WINDOWS_H
45 #include <windows.h>
46 #endif
47 #ifdef RUDIMENTS_HAVE_TLHELP32_H
48 #include <tlhelp32.h>
49 #endif
50 #ifdef RUDIMENTS_HAVE_DBGHELP_H
51 #include <dbghelp.h>
52 #endif
53
54 // for umask
55 #ifdef RUDIMENTS_HAVE_SYS_STAT_H
56 #include <sys/stat.h>
57 #endif
58
59 // for GetCurrentProcessId on windows
60 #ifdef RUDIMENTS_HAVE_PROCESS_H
61 #include <process.h>
62 #endif
63
64 // for waitpid...
65 #if defined(RUDIMENTS_HAVE_SYS_WAIT_H)
66 #include <sys/wait.h>
67 #endif
68
69 signalhandler process::_deadchildhandler;
70 signalhandler process::_shutdownhandler;
71 signalhandler process::_crashhandler;
72
73 void (*process::_shutdownfunc)(int32_t);
74 void (*process::_crashfunc)(int32_t);
75
76 bool process::_retry=true;
77
getProcessId()78 pid_t process::getProcessId() {
79 #if defined(RUDIMENTS_HAVE_GETCURRENTPROCESSID)
80 return GetCurrentProcessId();
81 #elif defined(RUDIMENTS_HAVE_GETPID)
82 return getpid();
83 #else
84 #error no getpid or anything like it
85 #endif
86 }
87
getParentProcessId()88 pid_t process::getParentProcessId() {
89 #if defined(RUDIMENTS_HAVE_GETPPID)
90 return getppid();
91 #elif defined(RUDIMENTS_HAVE_PROCESS32FIRST) && _WIN32_WINNT>=0x0500
92 HANDLE snap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
93 if (snap==INVALID_HANDLE_VALUE) {
94 return -1;
95 }
96 PROCESSENTRY32 pe;
97 pe.dwSize=sizeof(pe);
98 pid_t mypid=getProcessId();
99 pid_t ppid=-1;
100 if (Process32First(snap,&pe)) {
101 do {
102 if (pe.th32ProcessID==mypid) {
103 ppid=pe.th32ParentProcessID;
104 break;
105 }
106 } while (Process32Next(snap,&pe));
107 }
108 CloseHandle(snap);
109 return ppid;
110 #else
111 RUDIMENTS_SET_ENOSYS
112 return 0;
113 #endif
114 }
115
getProcessGroupId()116 pid_t process::getProcessGroupId() {
117 return getProcessGroupId(0);
118 }
119
getProcessGroupId(pid_t pid)120 pid_t process::getProcessGroupId(pid_t pid) {
121 #if defined(RUDIMENTS_HAVE_GETPGID)
122 return getpgid(pid);
123 #else
124 // minix doesn't have the notion of process groups
125 // windows does, but they don't appear to have ID's
126 RUDIMENTS_SET_ENOSYS
127 return -1;
128 #endif
129 }
130
setProcessGroupId()131 bool process::setProcessGroupId() {
132 return setProcessGroupId(0);
133 }
134
setProcessGroupId(pid_t pgid)135 bool process::setProcessGroupId(pid_t pgid) {
136 return setProcessGroupId(0,pgid);
137 }
138
setProcessGroupId(pid_t pid,pid_t pgid)139 bool process::setProcessGroupId(pid_t pid, pid_t pgid) {
140 #if defined(RUDIMENTS_HAVE_SETPGID)
141 return !setpgid(pid,pgid);
142 #else
143 // minix doesn't have the notion of process groups
144 // windows does, but they don't appear to have ID's
145 RUDIMENTS_SET_ENOSYS
146 return false;
147 #endif
148 }
149
getSessionId()150 pid_t process::getSessionId() {
151 return getSessionId(0);
152 }
153
getSessionId(pid_t pid)154 pid_t process::getSessionId(pid_t pid) {
155 #if defined(RUDIMENTS_HAVE_GETSID)
156 return getsid(pid);
157 #else
158 // windows doesn't have the notion of sessions
159 RUDIMENTS_SET_ENOSYS
160 return -1;
161 #endif
162 }
163
newSession()164 pid_t process::newSession() {
165 #ifdef RUDIMENTS_HAVE_SETSID
166 return setsid();
167 #else
168 // windows doesn't have the notion of sessions
169 RUDIMENTS_SET_ENOSYS
170 return -1;
171 #endif
172 }
173
getUserId()174 uid_t process::getUserId() {
175 #if defined(RUDIMENTS_HAVE_GETUID)
176 return getuid();
177 #elif defined(RUDIMENTS_HAVE_GETUSERNAME)
178 DWORD size=0;
179 GetUserName(NULL,&size);
180 if (size<1) {
181 return (uid_t)-1;
182 }
183 CHAR *buffer=new CHAR[size];
184 if (GetUserName(buffer,&size)==FALSE) {
185 delete[] buffer;
186 return (uid_t)-1;
187 }
188 uid_t uid=userentry::getUserId(buffer);
189 delete[] buffer;
190 return uid;
191 #else
192 #error no getuid or anything like it
193 #endif
194 }
195
getEffectiveUserId()196 uid_t process::getEffectiveUserId() {
197 #if defined(RUDIMENTS_HAVE_GETEUID)
198 return geteuid();
199 #else
200 // windows doesn't have the notion of effective vs. actual
201 // user id's, so we'll return the actual user id
202 return getUserId();
203 #endif
204 }
205
setUserId(uid_t uid)206 bool process::setUserId(uid_t uid) {
207 #if defined(RUDIMENTS_HAVE_SETUID)
208 #if defined(RUDIMENTS_HAVE_SETGROUPS)
209 // If we're changing from root to another user, be sure
210 // to drop all group permissions first. This will fail
211 // if we're not root, so there's no need to check the
212 // result.
213 setgroups(0,NULL);
214 #endif
215 return !setuid(uid);
216 #else
217 RUDIMENTS_SET_ENOSYS
218 return false;
219 #endif
220 }
221
setUser(const char * username)222 bool process::setUser(const char *username) {
223 uid_t userid=userentry::getUserId(username);
224 return (userid!=(uid_t)-1)?setUserId(userid):true;
225 }
226
227 #ifdef RUDIMENTS_HAVE_SETEUID_BUT_NOT_DEFINED
228 extern "C" int seteuid(uid_t euid);
229 #endif
230
setEffectiveUserId(uid_t uid)231 bool process::setEffectiveUserId(uid_t uid) {
232 #if defined(RUDIMENTS_HAVE_SETEUID)
233 return !seteuid(uid);
234 #elif defined(RUDIMENTS_HAVE_SYSCALL_SETEUID)
235 return !syscall(SYS_seteuid,uid);
236 #else
237 // windows doesn't have the notion of effective user id's
238 RUDIMENTS_SET_ENOSYS
239 return false;
240 #endif
241 }
242
setEffectiveUser(const char * username)243 bool process::setEffectiveUser(const char *username) {
244 uid_t userid=userentry::getUserId(username);
245 return (userid!=(uid_t)-1)?setEffectiveUserId(userid):true;
246 }
247
setRealAndEffectiveUserId(uid_t uid,uid_t euid)248 bool process::setRealAndEffectiveUserId(uid_t uid, uid_t euid) {
249 #if defined(RUDIMENTS_HAVE_SETREUID)
250 return !setreuid(uid,euid);
251 #else
252 return setUserId(uid) && setEffectiveUserId(euid);
253 #endif
254 }
255
getGroupId()256 gid_t process::getGroupId() {
257 #if defined(RUDIMENTS_HAVE_GETUID)
258 return getgid();
259 #elif defined(RUDIMENTS_HAVE_GETUSERNAME)
260 // windows doesn't have the notion of group-thread association
261 // but we can return the primary group of the user associated
262 // with the thread
263 userentry uent;
264 uent.initialize(getUserId());
265 return uent.getPrimaryGroupId();
266 #else
267 #error no getgid or anything like it
268 #endif
269 }
270
getEffectiveGroupId()271 gid_t process::getEffectiveGroupId() {
272 #if defined(RUDIMENTS_HAVE_GETEGID)
273 return getegid();
274 #else
275 // windows doesn't have the notion of effective vs. actual
276 // group id's, so we'll return the actual group id
277 return getGroupId();
278 #endif
279 }
280
setGroupId(gid_t gid)281 bool process::setGroupId(gid_t gid) {
282 #if defined(RUDIMENTS_HAVE_SETUID)
283 return !setgid(gid);
284 #else
285 RUDIMENTS_SET_ENOSYS
286 return false;
287 #endif
288 }
289
setGroup(const char * groupname)290 bool process::setGroup(const char *groupname) {
291 gid_t groupid=groupentry::getGroupId(groupname);
292 return (groupid!=(gid_t)-1)?setGroupId(groupid):true;
293 }
294
295 #ifdef RUDIMENTS_HAVE_SETEGID_BUT_NOT_DEFINED
296 extern "C" int setegid(gid_t egid);
297 #endif
298
setEffectiveGroupId(gid_t gid)299 bool process::setEffectiveGroupId(gid_t gid) {
300 #if defined(RUDIMENTS_HAVE_SETEGID)
301 return !setegid(gid);
302 #else
303 // windows doesn't have the notion of effective group id's
304 RUDIMENTS_SET_ENOSYS
305 return false;
306 #endif
307 }
308
setEffectiveGroup(const char * groupname)309 bool process::setEffectiveGroup(const char *groupname) {
310 gid_t groupid=groupentry::getGroupId(groupname);
311 return (groupid!=(gid_t)-1)?setEffectiveGroupId(groupid):true;
312 }
313
setRealAndEffectiveGroupId(gid_t gid,gid_t egid)314 bool process::setRealAndEffectiveGroupId(gid_t gid, gid_t egid) {
315 #if defined(RUDIMENTS_HAVE_SETREUID)
316 return !setregid(gid,egid);
317 #else
318 return setGroupId(gid) && setEffectiveGroupId(egid);
319 #endif
320 }
321
setFileCreationMask(mode_t mask)322 mode_t process::setFileCreationMask(mode_t mask) {
323 #if defined(RUDIMENTS_HAVE_UMASK)
324 return ::umask(mask);
325 #else
326 RUDIMENTS_SET_ENOSYS
327 return 0;
328 #endif
329 }
330
fork()331 pid_t process::fork() {
332 #if defined(RUDIMENTS_HAVE_FORK)
333 pid_t result;
334 error::clearError();
335 do {
336 result=::fork();
337 if (result==-1 &&
338 error::getErrorNumber()==EAGAIN && _retry) {
339 snooze::macrosnooze(1);
340 continue;
341 }
342 } while (result==-1 && error::getErrorNumber()==EINTR);
343 return result;
344 #else
345 RUDIMENTS_SET_ENOSYS
346 return -1;
347 #endif
348 }
349
supportsFork()350 bool process::supportsFork() {
351 #if defined(RUDIMENTS_HAVE_FORK)
352 return true;
353 #else
354 return false;
355 #endif
356 }
357
exec(const char * command,const char * const * args)358 bool process::exec(const char *command, const char * const *args) {
359 #if defined(RUDIMENTS_HAVE_EXECVP)
360 return execvp(command,(char * const *)args)!=-1;
361 #elif defined(RUDIMENTS_HAVE_CREATE_PROCESS)
362 if (spawn(command,args,false)) {
363 exit(0);
364 }
365 return false;
366 #else
367 RUDIMENTS_SET_ENOSYS
368 return false;
369 #endif
370 }
371
372 #if defined(RUDIMENTS_HAVE_CREATE_PROCESS)
quoteArg(const char * arg)373 static char *quoteArg(const char *arg) {
374
375 // put quotes around args that contain spaces...
376 if (!charstring::contains(arg," ")) {
377 return charstring::duplicate(arg);
378 }
379
380 size_t quotedarglen=charstring::length(arg);
381 char *quotedarg=new char[charstring::length(arg)+4];
382
383 charstring::copy(quotedarg,"\"");
384 charstring::append(quotedarg,arg);
385
386 // if the last character is a backslash, then escape it using another
387 // backslash, otherwise windows will think that the trailing \"
388 // means "include a literal quote"
389 if (arg[quotedarglen-1]=='\\') {
390 charstring::append(quotedarg,"\\");
391 }
392
393 charstring::append(quotedarg,"\"");
394
395 return quotedarg;
396 }
397 #endif
398
spawn(const char * command,const char * const * args,bool detached)399 pid_t process::spawn(const char *command,
400 const char * const *args,
401 bool detached) {
402 #if defined(RUDIMENTS_HAVE_FORK) && defined(RUDIMENTS_HAVE_EXECVP)
403 pid_t child=fork();
404 if (child==-1 || child>0) {
405 return child;
406 }
407 return exec(command,args);
408 #elif defined(RUDIMENTS_HAVE_CREATE_PROCESS)
409
410 // get the fully qualified (and then shortened)
411 // version of the command
412 char *shortcommand=fullyQualifiedCommand(command);
413
414 // are we trying to run a batch file?
415 size_t shortcommandlen=charstring::length(shortcommand);
416 bool batchfile=
417 (shortcommandlen>=4 &&
418 !charstring::compareIgnoringCase(
419 shortcommand+shortcommandlen-4,".bat"));
420
421 // Create the command line from the args...
422 // CreateProcess must be able to modify the command line (for
423 // some reason, and there's no indication as to whether it could
424 // try to use more bytes than originally passed in) and will
425 // only accept a command line of 32768 chars or less.
426 stringbuffer cmdl;
427 size_t spaceleft=32768;
428
429 // If we're trying to run a batch file then we need to use
430 // cmd.exe as our command and use /c batchfilename [args]
431 // as the args, so append /c and the batch file name first.
432 if (batchfile) {
433 cmdl.append("/c ");
434 spaceleft-=3;
435 if (spaceleft>shortcommandlen) {
436 cmdl.append(shortcommand);
437 spaceleft-=shortcommandlen;
438 }
439 }
440
441 // append the args
442 if (args) {
443 bool first=true;
444 for (const char * const *arg=args; *arg; arg++) {
445 if (!first || batchfile) {
446 if (spaceleft>1) {
447 cmdl.append(" ");
448 spaceleft--;
449 } else {
450 break;
451 }
452 }
453 if (first) {
454 first=false;
455 }
456 char *qarg=quoteArg(*arg);
457 size_t size=charstring::length(qarg);
458 if (spaceleft>size) {
459 cmdl.append(qarg);
460 delete[] qarg;
461 spaceleft-=size;
462 } else {
463 delete[] qarg;
464 break;
465 }
466 }
467 }
468 char *commandline=cmdl.detachString();
469
470 // replace the command with (fully qualified and shortened)
471 // cmd.exe if we're trying to run a batch file
472 if (batchfile) {
473 char *shortcmd=fullyQualifiedCommand("cmd.exe");
474 delete[] shortcommand;
475 shortcommand=shortcmd;
476 }
477
478 // create the new process and return it's pid on success
479 STARTUPINFO si;
480 PROCESS_INFORMATION pi;
481 bytestring::zero(&si,sizeof(si));
482 bytestring::zero(&pi,sizeof(pi));
483 bool success=(CreateProcess(shortcommand,commandline,
484 NULL,NULL,TRUE,
485 (detached)?CREATE_NEW_PROCESS_GROUP:0,
486 NULL,NULL,&si,&pi)==TRUE);
487 delete[] shortcommand;
488 delete[] commandline;
489 return (success)?pi.dwProcessId:-1;
490 #else
491 RUDIMENTS_SET_ENOSYS
492 return false;
493 #endif
494 }
495
fullyQualifiedCommand(const char * command)496 char *process::fullyQualifiedCommand(const char *command) {
497
498 #if defined(RUDIMENTS_HAVE_CREATE_PROCESS)
499 // if the command doesn't include a backslash then search
500 // the path for it
501 char *fqcommand=NULL;
502 if (charstring::contains(command,"\\")) {
503 fqcommand=charstring::duplicate(command);
504 } else {
505
506 // split the PATH on ;'s
507 const char *path=environment::getValue("PATH");
508 char **dirs=NULL;
509 uint64_t dircount=0;
510 charstring::split(path,";",true,&dirs,&dircount);
511
512 // search each directory in the PATH
513 // (actually, try the current directory first, it's not
514 // in the PATH, but should be searched first)
515 size_t cmdlen=charstring::length(command);
516 for (uint64_t i=0; i<=dircount; i++) {
517
518 char *dir=NULL;
519 if (!i) {
520 dir=directory::
521 getCurrentWorkingDirectory();
522 } else {
523 dir=dirs[i-1];
524 }
525
526 fqcommand=new char[
527 charstring::length(dir)+1+
528 cmdlen+1];
529
530 charstring::copy(fqcommand,dir);
531 charstring::append(fqcommand,"\\");
532 charstring::append(fqcommand,command);
533
534 delete[] dir;
535
536 if (file::exists(fqcommand)) {
537 // if we found the command in the
538 // current directory, then don't
539 // prepend the directory to it,
540 // just use it as-is
541 if (!i) {
542 delete[] fqcommand;
543 fqcommand=charstring::
544 duplicate(command);
545 }
546 break;
547 }
548
549 delete[] fqcommand;
550 fqcommand=NULL;
551 }
552
553 // clean up
554 delete[] dirs;
555 }
556 if (!fqcommand) {
557 return NULL;
558 }
559
560 // get the short form of the fully qualified command
561 // (this will work on older versions of windows)
562 size_t shortcommandsize=charstring::length(fqcommand)+1;
563 char *shortcommand=new char[shortcommandsize];
564 GetShortPathName(fqcommand,shortcommand,shortcommandsize);
565 delete[] fqcommand;
566
567 return shortcommand;
568 #else
569 return NULL;
570 #endif
571 }
572
detach()573 bool process::detach() {
574
575 // fork off a child process
576 pid_t result=fork();
577 if (result==-1) {
578 return false;
579 }
580
581 // let the parent process exit
582 if (result) {
583 // cygwin needs a sleep or both processes will exit
584 #ifdef __CYGWIN__
585 snooze::macrosnooze(1);
586 #endif
587 exitImmediately(0);
588 }
589
590 // create a new session with no controlling terminal
591 newSession();
592
593 // change directory to root to avoid keeping any directories in use
594 directory::changeDirectory("/");
595
596 // Set umask such that files are created 666 and directories 777. This
597 // way we can change them to whatever we like using chmod(). We want to
598 // avoid inheriting a umask which wouldn't give us write permissions to
599 // files we create.
600 setFileCreationMask(0);
601
602 return true;
603 }
604
exit(int32_t status)605 void process::exit(int32_t status) {
606 ::exit(status);
607 }
608
exitImmediately(int32_t status)609 void process::exitImmediately(int32_t status) {
610 _exit(status);
611 }
612
sendSignal(pid_t processid,int32_t signum)613 bool process::sendSignal(pid_t processid, int32_t signum) {
614 #ifdef RUDIMENTS_HAVE_KILL
615 int32_t result;
616 error::clearError();
617 do {
618 result=kill(processid,signum);
619 } while (result==-1 && error::getErrorNumber()==EINTR);
620 return !result;
621 #elif defined(RUDIMENTS_HAVE_GENERATECONSOLECTRLEVENT)
622
623 // decide what access rights we need
624 DWORD accessrights=PROCESS_TERMINATE;
625 if (signum!=SIGKILL) {
626 accessrights=PROCESS_CREATE_THREAD|
627 PROCESS_QUERY_INFORMATION|
628 PROCESS_VM_OPERATION|
629 PROCESS_VM_WRITE|
630 PROCESS_VM_READ;
631 }
632
633 // open the target process
634 HANDLE targetprocess=OpenProcess(accessrights,FALSE,processid);
635 if (!targetprocess) {
636 return false;
637 }
638
639 // for SIGKILL we just need to call TerminateProcess
640 if (signum==SIGKILL) {
641 bool result=(TerminateProcess(targetprocess,1)!=0);
642 CloseHandle(targetprocess);
643 return result;
644 }
645
646 // For SIGABRT, SIGFPE, SIGILL and SIGSEGV we can trigger the
647 // target process' unhandled exception filter by creating a
648 // thread aimed at NULL.
649 if (signum==SIGABRT || signum==SIGFPE ||
650 signum==SIGILL || signum==SIGSEGV) {
651 HANDLE otherthread=
652 CreateRemoteThread(targetprocess,
653 NULL,0,
654 (LPTHREAD_START_ROUTINE)NULL,
655 NULL,0,NULL);
656 if (otherthread) {
657 CloseHandle(otherthread);
658 }
659 CloseHandle(targetprocess);
660 return otherthread!=NULL;
661 }
662
663 // For SIGINT/SIGTERM, it gets a lot crazier...
664
665 // Yes, the ridiculousness below is the only "reasonable"
666 // way to do this...
667
668 // First... SIGINT is really CTRL-C, but processes created with
669 // the CREATE_NEW_PROCESS_GROUP flag don't respond to CTRL-C.
670 // All processes respond to CTRL-BREAK though, so we need to
671 // use that instead.
672
673 // Ideally for SIGINT/SIGTERM we'd just run
674 // GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,processid) but that
675 // only works if the calling process is in the same process
676 // group as processid (ie. a parent or child of processid).
677 //
678 // So, we need to somehow coerce a process or thread in the
679 // target process group to run it for us. We can create a new
680 // thread in the target process using CreateRemoteThread.
681 //
682 // Ideally we'd just tell it to run
683 // GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,0) but
684 // CreateRemoteThread only allows you to pass one argument
685 // to the function that it runs and we need to pass two.
686 //
687 // The only "obvious" way to do this is to do define a chunk
688 // of memory containing the machine code for a function that
689 // runs GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,0) and copy it
690 // over to the target process.
691 //
692 // Then we can create a thread over there and aim the thread at
693 // our function.
694
695 // this only works on x86 and x64, so bail right away if
696 // this isn't one of those platforms
697 char *arch=sys::getOperatingSystemArchitecture();
698 if (!charstring::compare(arch,"Unknown")) {
699 return false;
700 }
701
702 // Get the address of GenerateConsoleCtrlEvent in kernel32.dll.
703 // kernel32.dll is loaded at the same address for all programs,
704 // so the address of this function ought to be the same in
705 // the target process as it is here.
706 HMODULE kernel32=GetModuleHandle("Kernel32");
707 if (!kernel32) {
708 return false;
709 }
710 FARPROC funcaddr=GetProcAddress(kernel32,
711 "GenerateConsoleCtrlEvent");
712 if (!funcaddr) {
713 return false;
714 }
715
716 // Define a chunk of memory containing the machine code for
717 // a function that runs GenerateConsoleCtrlEvent with two
718 // parameters (with values of 0). Eventually this code will
719 // be run in the target process...
720 //
721 // helpful site:
722 // https://defuse.ca/online-x86-assembler.htm
723
724 const unsigned char *updatedmachinecode=NULL;
725 size_t machinecodesize=0;
726
727 // FIXME: use better method of detecting x86 vs. x64
728 #ifdef _USE_32BIT_TIME_T
729
730 // for x86:
731 const unsigned char machinecode32[]={
732 // load second parameter (0)
733 // (we'll overwrite this in a minute)
734 0x68, // push (word)
735 0x0,0x0,0x0,0x0, // 0
736 // load first parameter (1)
737 0x6A, // push (byte)
738 0x1, // 1
739 // load the absolute address of the function to
740 // call (for now use 0, we'll overwrite this in
741 // a minute)
742 0xB8, // mov eax
743 0x0,0x0,0x0,0x0, // 0
744 // call the function
745 0xFF,0xD0, // call eax
746 // return 1
747 0xB8, // mov eax
748 0x1,0x0,0x0,0x0, // 1
749 // return
750 0xC3 // ret
751 };
752 size_t machinecode32size=sizeof(machinecode32);
753
754 // copy the code into a buffer and
755 // replace the second parameter and call address
756 unsigned char *updatedmachinecode32=
757 (unsigned char *)bytestring::duplicate(
758 machinecode32,
759 machinecode32size);
760
761 uint32_t *addr=
762 (uint32_t *)(updatedmachinecode32+1);
763 *addr=(uint32_t)processid;
764
765 addr=(uint32_t *)(updatedmachinecode32+8);
766 *addr=(uint32_t)funcaddr;
767
768 updatedmachinecode=updatedmachinecode32;
769 machinecodesize=machinecode32size;
770
771 #else
772
773 // for x64:
774 const unsigned char machinecode64[]={
775 // allocate shadow space of 32 bytes on the
776 // stack and align it to 16 bytes
777 0x48,0x83,0xEC, // sub rsp
778 0x28, // 0x28
779 // load second parameter (0)
780 // (we'll overwrite this in a minute)
781 0x48,0xC7,0xC2, // mov rdx
782 0x00,0x00,0x00,0x00, // 0
783 // load first parameter (0)
784 0x48,0xC7,0xC1, // mov rcx
785 0x01,0x00,0x00,0x00, // 1
786 // load the absolute address of the function to
787 // call (for now use 0, we'll overwrite this in
788 // a minute)
789 0x49,0xBA, // movabs r10
790 0x00,0x00,0x00,0x00, // 0
791 0x00,0x00,0x00,0x00, // 0 (64-bit)
792 // call the function
793 0x41,0xFF,0xD2, // call r10
794 // return 1
795 0x48,0xC7,0xC0, // mov rax
796 0x01,0x00,0x00,0x00, // 1
797 // deallocate the shadow space
798 // and align the stack to 16 bytes
799 0x48,0x83,0xC4, // add rsp
800 0x28, // 0x28
801 // return
802 0xC3 // retq
803 };
804 size_t machinecode64size=sizeof(machinecode64);
805
806 // copy the code into a buffer and
807 // replace the second parameter and call address
808 unsigned char *updatedmachinecode64=
809 (unsigned char *)bytestring::duplicate(
810 machinecode64,
811 machinecode64size);
812
813 uint32_t *addr32=
814 (uint32_t *)(updatedmachinecode64+7);
815 *addr32=(uint32_t)processid;
816
817 uint64_t *addr64=
818 (uint64_t *)(updatedmachinecode64+20);
819 *addr64=(uint64_t)funcaddr;
820
821 updatedmachinecode=updatedmachinecode64;
822 machinecodesize=machinecode64size;
823 #endif
824
825 // allocate memory in the target process to copy the
826 // machine code into
827 void *codetorun=VirtualAllocEx(targetprocess,
828 NULL,machinecodesize,
829 MEM_COMMIT,
830 PAGE_EXECUTE_READWRITE);
831 if (!codetorun) {
832 CloseHandle(targetprocess);
833 return false;
834 }
835
836 // copy the machine code over to the target process
837 if (!WriteProcessMemory(targetprocess,
838 codetorun,
839 updatedmachinecode,
840 machinecodesize,
841 NULL)) {
842 CloseHandle(targetprocess);
843 return false;
844 }
845
846 // create a thread in the target process and aim it at
847 // the machine code we passed over there
848 HANDLE otherthread=
849 CreateRemoteThread(targetprocess,
850 NULL,0,
851 (LPTHREAD_START_ROUTINE)codetorun,
852 NULL,0,NULL);
853 if (!otherthread) {
854 CloseHandle(targetprocess);
855 return false;
856 }
857
858 // wait for the thread to finish running
859 WaitForSingleObject(otherthread,INFINITE);
860
861 // clean up
862 VirtualFreeEx(targetprocess,codetorun,0,MEM_RELEASE);
863 CloseHandle(otherthread);
864 CloseHandle(targetprocess);
865
866 return true;
867 #else
868 RUDIMENTS_SET_ENOSYS
869 return false;
870 #endif
871 }
872
raiseSignal(int32_t signum)873 bool process::raiseSignal(int32_t signum) {
874 #if defined(RUDIMENTS_HAVE_GENERATECONSOLECTRLEVENT)
875 switch (signum) {
876 case SIGINT:
877 case SIGTERM:
878 // SIGINT is really CTRL-C, but processes
879 // created with the CREATE_NEW_PROCESS_GROUP
880 // flag don't respond to CTRL-C. All processes
881 // respond to CTRL-BREAK though, so we need to
882 // use that instead.
883 if (GenerateConsoleCtrlEvent(
884 CTRL_BREAK_EVENT,0)) {
885 return true;
886 }
887 case SIGKILL:
888 if (TerminateProcess(
889 INVALID_HANDLE_VALUE,1)) {
890 return true;
891 }
892 case SIGFPE:
893 {
894 // crash on purpose
895 uint16_t a=1;
896 uint16_t b=0;
897 uint16_t c=a/b;
898 }
899 case SIGABRT:
900 case SIGILL:
901 case SIGSEGV:
902 {
903 // crash on purpose
904 void (*f)(void)=NULL;
905 f();
906 }
907 }
908 return false;
909 #elif defined(RUDIMENTS_HAVE_RAISE)
910 int32_t result;
911 error::clearError();
912 do {
913 result=raise(signum);
914 } while (result==-1 && error::getErrorNumber()==EINTR);
915 return !result;
916 #else
917 RUDIMENTS_SET_ENOSYS
918 return false;
919 #endif
920 }
921
atExit(void (* function)(void))922 bool process::atExit(void (*function)(void)) {
923 #ifdef RUDIMENTS_HAVE_ATEXIT
924 return !atexit(function);
925 #else
926 return false;
927 #endif
928 }
929
createPidFile(const char * filename,mode_t permissions)930 bool process::createPidFile(const char *filename, mode_t permissions) {
931 char *pid=charstring::parseNumber((uint64_t)process::getProcessId());
932 file pidfile;
933 bool retval=(pidfile.create(filename,permissions) &&
934 pidfile.write(pid)==(ssize_t)charstring::length(pid));
935 delete[] pid;
936 return retval;
937 }
938
checkForPidFile(const char * filename)939 int64_t process::checkForPidFile(const char *filename) {
940 char *pidstring=file::getContents(filename);
941 int64_t retval=(pidstring && pidstring[0])?
942 charstring::toInteger(pidstring):-1;
943 delete[] pidstring;
944 return retval;
945 }
946
exitOnCrashOrShutDown()947 void process::exitOnCrashOrShutDown() {
948 exitOnShutDown();
949 exitOnCrash();
950 }
951
exitOnShutDown()952 void process::exitOnShutDown() {
953 _shutdownhandler.setHandler(defaultShutDown);
954 _shutdownhandler.handleSignal(SIGINT);
955 _shutdownhandler.handleSignal(SIGTERM);
956 #ifdef SIGQUIT
957 _shutdownhandler.handleSignal(SIGQUIT);
958 #endif
959 #ifdef SIGHUP
960 _shutdownhandler.handleSignal(SIGHUP);
961 #endif
962 }
963
handleShutDown(void (* shutdownfunction)(int32_t))964 void process::handleShutDown(void (*shutdownfunction)(int32_t)) {
965 _shutdownfunc=shutdownfunction;
966 _shutdownhandler.setHandler(shutDown);
967 _shutdownhandler.handleSignal(SIGINT);
968 _shutdownhandler.handleSignal(SIGTERM);
969 #ifdef SIGQUIT
970 _shutdownhandler.handleSignal(SIGQUIT);
971 #endif
972 #ifdef SIGHUP
973 _shutdownhandler.handleSignal(SIGHUP);
974 #endif
975 }
976
exitOnCrash()977 void process::exitOnCrash() {
978 _crashhandler.setHandler(defaultCrash);
979 _crashhandler.handleSignal(SIGABRT);
980 _crashhandler.handleSignal(SIGFPE);
981 _crashhandler.handleSignal(SIGILL);
982 _crashhandler.handleSignal(SIGSEGV);
983 #ifdef SIGBUS
984 _crashhandler.handleSignal(SIGBUS);
985 #endif
986 #ifdef SIGIOT
987 _crashhandler.handleSignal(SIGIOT);
988 #endif
989 #ifdef SIGEMT
990 _crashhandler.handleSignal(SIGEMT);
991 #endif
992 #ifdef SIGSYS
993 _crashhandler.handleSignal(SIGSYS);
994 #endif
995 }
996
handleCrash(void (* crashfunction)(int32_t))997 void process::handleCrash(void (*crashfunction)(int32_t)) {
998 _crashfunc=crashfunction;
999 _crashhandler.setHandler(crash);
1000 _crashhandler.handleSignal(SIGABRT);
1001 _crashhandler.handleSignal(SIGFPE);
1002 _crashhandler.handleSignal(SIGILL);
1003 _crashhandler.handleSignal(SIGSEGV);
1004 #ifdef SIGBUS
1005 _crashhandler.handleSignal(SIGBUS);
1006 #endif
1007 #ifdef SIGIOT
1008 _crashhandler.handleSignal(SIGIOT);
1009 #endif
1010 #ifdef SIGEMT
1011 _crashhandler.handleSignal(SIGEMT);
1012 #endif
1013 #ifdef SIGSYS
1014 _crashhandler.handleSignal(SIGSYS);
1015 #endif
1016 }
1017
waitForChildren()1018 void process::waitForChildren() {
1019 #ifdef SIGCHLD
1020 _deadchildhandler.setHandler(waitForChildrenToExit);
1021 _deadchildhandler.addFlag(SA_NOCLDSTOP);
1022 _deadchildhandler.handleSignal(SIGCHLD);
1023 #endif
1024 }
1025
dontWaitForChildren()1026 void process::dontWaitForChildren() {
1027 #ifdef SIGCHLD
1028 _deadchildhandler.setHandler((void (*)(int32_t))SIG_DFL);
1029 _deadchildhandler.removeAllFlags();
1030 _deadchildhandler.handleSignal(SIGCHLD);
1031 #endif
1032 }
1033
shutDown(int32_t signum)1034 void process::shutDown(int32_t signum) {
1035 (*_shutdownfunc)(signum);
1036 }
1037
crash(int32_t signum)1038 void process::crash(int32_t signum) {
1039 (*_crashfunc)(signum);
1040 }
1041
defaultShutDown(int32_t signum)1042 void process::defaultShutDown(int32_t signum) {
1043 process::exit(0);
1044 }
1045
defaultCrash(int32_t signum)1046 void process::defaultCrash(int32_t signum) {
1047 process::exit(1);
1048 }
1049
waitForChildrenToExit(int32_t signum)1050 void process::waitForChildrenToExit(int32_t signum) {
1051
1052 // Some systems generate a single SIGCHLD even if more than 1 child
1053 // has entered it's exit state, so we need to loop here and catch
1054 // all of them.
1055
1056 // If getChildStateChange returns 0 then there were no more
1057 // processes in their exit state, the loop should exit.
1058 // If it returns > 0 then it successfully waited on a process and it
1059 // should loop back to wait on another one.
1060 // If it returns -1 then there was some error and the loop should exit.
1061 do {} while (getChildStateChange(-1,false,true,true,
1062 NULL,NULL,NULL,NULL)>0);
1063
1064 // FIXME: What if a SIGCHLD gets generated after waitpid() has returned
1065 // but before the signal handler exits? Will that SIGCHLD be lost?
1066
1067 // Since we didn't use the SA_ONESHOT flag when we set up this signal
1068 // handler, we don't need to reinstall the signal handler here, it will
1069 // be done automatically.
1070 }
1071
wait(pid_t pid)1072 bool process::wait(pid_t pid) {
1073 return wait(pid,NULL);
1074 }
1075
wait(pid_t pid,int32_t * exitstatus)1076 bool process::wait(pid_t pid, int32_t *exitstatus) {
1077 #ifdef _WIN32
1078 HANDLE h=OpenProcess(SYNCHRONIZE,TRUE,pid);
1079 if (!h) {
1080 return false;
1081 }
1082
1083 bool retval=(WaitForSingleObject(h,INFINITE)==WAIT_OBJECT_0);
1084
1085 if (retval && exitstatus) {
1086 DWORD status;
1087 if (GetExitCodeProcess(h,&status)) {
1088 *exitstatus=status;
1089 } else {
1090 retval=false;
1091 }
1092 }
1093
1094 CloseHandle(h);
1095
1096 return retval;
1097 #else
1098 return (getChildStateChange(pid,true,true,true,
1099 NULL,exitstatus,NULL,NULL)==pid);
1100 #endif
1101 }
1102
getChildStateChange(pid_t pid,bool wait,bool ignorestop,bool ignorecontinue,childstatechange * newstate,int32_t * exitstatus,int32_t * signum,bool * coredump)1103 pid_t process::getChildStateChange(pid_t pid,
1104 bool wait,
1105 bool ignorestop,
1106 bool ignorecontinue,
1107 childstatechange *newstate,
1108 int32_t *exitstatus,
1109 int32_t *signum,
1110 bool *coredump) {
1111 #ifdef _WIN32
1112 RUDIMENTS_SET_ENOSYS
1113 return -1;
1114 #else
1115
1116 // build options
1117 int32_t options=0;
1118 if (!wait) {
1119 options|=WNOHANG;
1120 }
1121 if (!ignorestop) {
1122 options|=WUNTRACED;
1123 }
1124 if (!ignorecontinue) {
1125 #ifdef WCONTINUED
1126 options|=WCONTINUED;
1127 #endif
1128 }
1129
1130 // init status
1131 int32_t status=0;
1132
1133 // wait
1134 int32_t childpid=-1;
1135 error::clearError();
1136 do {
1137 // Minix 3.1.8 needs the int * cast
1138 childpid=waitpid(pid,(int *)&status,options);
1139 } while (childpid==-1 && error::getErrorNumber()==EINTR);
1140
1141 // set return values
1142 if (childpid>0 && newstate) {
1143 if (WIFEXITED(status)) {
1144 *newstate=EXIT_CHILDSTATECHANGE;
1145 if (exitstatus) {
1146 *exitstatus=WEXITSTATUS(status);
1147 }
1148 } else if (WIFSIGNALED(status)) {
1149 *newstate=TERMINATED_CHILDSTATECHANGE;
1150 if (signum) {
1151 *signum=WTERMSIG(status);
1152 }
1153 #ifdef WCOREDUMP
1154 if (coredump) {
1155 *coredump=WCOREDUMP(status);
1156 }
1157 #endif
1158 } else if (WIFSTOPPED(status)) {
1159 *newstate=STOPPED_CHILDSTATECHANGE;
1160 if (signum) {
1161 *signum=WSTOPSIG(status);
1162 }
1163 #ifdef WIFCONTINUED
1164 } else if (WIFCONTINUED(status)) {
1165 *newstate=CONTINUED_CHILDSTATECHANGE;
1166 #endif
1167 }
1168 }
1169
1170 return childpid;
1171 #endif
1172 }
1173
supportsGetChildStateChange()1174 bool process::supportsGetChildStateChange() {
1175 #ifdef _WIN32
1176 return false;
1177 #else
1178 return true;
1179 #endif
1180 }
1181
retryFailedFork()1182 void process::retryFailedFork() {
1183 _retry=true;
1184 }
1185
dontRetryFailedFork()1186 void process::dontRetryFailedFork() {
1187 _retry=false;
1188 }
1189
getRetryFailedFork()1190 bool process::getRetryFailedFork() {
1191 return _retry;
1192 }
1193
backtrace(output * out,uint32_t maxframes)1194 void process::backtrace(output *out, uint32_t maxframes) {
1195 #if defined(RUDIMENTS_HAVE_BACKTRACE)
1196 unsigned char **btarray=new unsigned char *[maxframes];
1197 size_t btsize=::backtrace((void **)btarray,(int)maxframes);
1198 char **btstrings=backtrace_symbols((void **)btarray,btsize);
1199 for (size_t i=0; i<btsize; i++) {
1200 out->write(btstrings[i]);
1201 out->write('\n');
1202 }
1203 delete[] btstrings;
1204 delete[] btarray;
1205 #elif defined(RUDIMENTS_HAVE_CAPTURESTACKBACKTRACE) && \
1206 _WIN32_WINVER >= 0x0600
1207 // CaptureStackBackTrace not supported prior to Vista
1208 HANDLE process=GetCurrentProcess();
1209 if (!SymInitialize(process,NULL,TRUE)) {
1210 return;
1211 }
1212 void **btarray=(void **)malloc(maxframes*sizeof(void *));
1213 WORD btsize=CaptureStackBackTrace(0,128,btarray,NULL);
1214 SYMBOL_INFO *symbolinfo=(SYMBOL_INFO *)
1215 malloc(sizeof(SYMBOL_INFO)+
1216 (1024*sizeof(TCHAR)));
1217 symbolinfo->MaxNameLen=1023;
1218 symbolinfo->SizeOfStruct=sizeof(SYMBOL_INFO);
1219 IMAGEHLP_LINE64 *line=(IMAGEHLP_LINE64 *)
1220 malloc(sizeof(IMAGEHLP_LINE64));
1221 line->SizeOfStruct=sizeof(IMAGEHLP_LINE64);
1222 DWORD displacement;
1223 for (WORD i=0; i<btsize; i++) {
1224 if (SymFromAddr(process,(DWORD64)btarray[i],
1225 NULL,symbolinfo)) {
1226 output->write("\?\?\?(");
1227 output->write(symbolinfo->Name);
1228 output->write(")[0x");
1229 output->write(symbolinfo->Address);
1230 output->write("]\n");
1231 }
1232 }
1233 free(line);
1234 free(symbolinfo);
1235 free(btarray);
1236 #endif
1237 }
1238
backtrace(output * out)1239 void process::backtrace(output *out) {
1240 backtrace(out,128);
1241 }
1242
backtrace(const char * filename)1243 void process::backtrace(const char *filename) {
1244 backtrace(filename,permissions::evalPermString("rw-------"),128);
1245 }
1246
backtrace(const char * filename,mode_t perms,uint32_t maxframes)1247 void process::backtrace(const char *filename,
1248 mode_t perms,
1249 uint32_t maxframes) {
1250 file f;
1251 if (f.open(filename,O_WRONLY|O_APPEND|O_CREAT,perms)) {
1252 backtrace(&f,maxframes);
1253 }
1254 }
1255