1 // Copyright (C) 1999-2005 Open Source Telecom Corporation.
2 // Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 // As a special exception, you may use this file as part of a free software
19 // library without restriction. Specifically, if other files instantiate
20 // templates or use macros or inline functions from this file, or you compile
21 // this file and link it with other files to produce an executable, this
22 // file does not by itself cause the resulting executable to be covered by
23 // the GNU General Public License. This exception does not however
24 // invalidate any other reasons why the executable file might be covered by
25 // the GNU General Public License.
26 //
27 // This exception applies only to the code released under the name GNU
28 // Common C++. If you copy code from other releases into a copy of GNU
29 // Common C++, as the General Public License permits, the exception does
30 // not apply to the code that you add in this way. To avoid misleading
31 // anyone as to the status of such modified files, you must delete
32 // this exception notice from them.
33 //
34 // If you write modifications of your own for GNU Common C++, it is your choice
35 // whether to permit this exception to apply to your modifications.
36 // If you do not wish that, delete this exception notice.
37 //
38
39 #include <cc++/config.h>
40 #include <cc++/export.h>
41 #include <cc++/thread.h>
42 #include <cc++/process.h>
43 #include <cc++/strchar.h>
44
45 #include <cstdlib>
46 #include <cstdio>
47 #include <cerrno>
48 #include <csignal>
49
50 #ifdef MACOSX
51 #undef _POSIX_PRIORITY_SCHEDULING
52 #endif
53
54 #ifndef WIN32
55 #ifdef HAVE_SYS_TIME_H
56 #include <sys/time.h>
57 #endif
58
59 #ifdef HAVE_SYS_WAIT_H
60 #include <sys/wait.h>
61 #endif
62
63 #include <pwd.h>
64 #include <grp.h>
65
66 #ifdef SIGTSTP
67 #include <sys/file.h>
68 #include <sys/ioctl.h>
69 #endif
70
71 #ifndef _PATH_TTY
72 #define _PATH_TTY "/dev/tty"
73 #endif
74 #endif
75
76 #ifdef WIN32
77 #include <process.h>
78 #include <stdio.h>
79 #endif
80
81 #ifdef CCXX_NAMESPACES
82 namespace ost {
83 #endif
84
85 static char *_pUser = NULL;
86 static char *_pHome = NULL;
87
88 bool Process::rtflag = false;
89
90 #ifdef WIN32
91
92 static SYSTEM_INFO sysinfo;
93 static LPSYSTEM_INFO lpSysInfo = NULL;
94
init_sysinfo(void)95 static void init_sysinfo(void)
96 {
97 if(!lpSysInfo) {
98 lpSysInfo = &sysinfo;
99 memset(&sysinfo, 0, sizeof(sysinfo));
100 GetSystemInfo(lpSysInfo);
101 }
102 }
103
getUser(void)104 const char *Process::getUser(void)
105 {
106 static char userid[65];
107 DWORD length = sizeof(userid);
108
109 if(GetUserName(userid, &length))
110 return userid;
111
112 return NULL;
113 }
114
getPageSize(void)115 size_t Process::getPageSize(void)
116 {
117 init_sysinfo();
118 return (size_t) lpSysInfo->dwPageSize;
119 }
120
spawn(const char * exename,const char ** args,bool wait)121 int Process::spawn(const char *exename, const char **args, bool wait)
122 {
123 int mode = P_NOWAIT;
124
125 if(wait)
126 mode = P_WAIT;
127
128 return (int)::spawnvp(mode, (char *)exename, (char **)args);
129 }
130
join(int pid)131 int Process::join(int pid)
132 {
133 int status, result;
134
135 if(pid == -1)
136 return pid;
137
138 result = (int)cwait(&status, pid, WAIT_CHILD);
139 if(status & 0x0)
140 return -1;
141 return result;
142 }
143
cancel(int pid,int sig)144 bool Process::cancel(int pid, int sig)
145 {
146 HANDLE hPid = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
147 bool rtn = true;
148
149 if(!hPid)
150 return false;
151
152 switch(sig) {
153 case SIGABRT:
154 case SIGTERM:
155 if(!TerminateProcess(hPid, -1))
156 rtn = false;
157 case 0:
158 break;
159 default:
160 rtn = false;
161 }
162 CloseHandle(hPid);
163 return rtn;
164 }
165
setPriority(int pri)166 void Process::setPriority(int pri)
167 {
168 DWORD pc = NORMAL_PRIORITY_CLASS;
169 DWORD pid = GetCurrentProcessId();
170 HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, TRUE, pid);
171
172 #ifdef BELOW_NORMAL_PRIORITY_CLASS
173 if(pri == -1)
174 pc = BELOW_NORMAL_PRIORITY_CLASS;
175 else if(pri == 1)
176 pc = ABOVE_NORMAL_PRIORITY_CLASS;
177 else
178 #endif
179 if(pri == 2)
180 pc = HIGH_PRIORITY_CLASS;
181 else if(pri > 2)
182 pc = REALTIME_PRIORITY_CLASS;
183 else if(pri < -1)
184 pc = IDLE_PRIORITY_CLASS;
185
186 SetPriorityClass(hProcess, pc);
187 CloseHandle(hProcess);
188 }
189
setScheduler(const char * cp)190 void Process::setScheduler(const char *cp)
191 {
192 if(!stricmp(cp, "fifo"))
193 setPriority(3);
194 else if(!stricmp(cp, "rr"))
195 setPriority(2);
196 else if(!stricmp(cp, "idle"))
197 setPriority(-2);
198 else
199 setPriority(0);
200 }
201
setRealtime(int pri)202 void Process::setRealtime(int pri)
203 {
204 setPriority(3);
205 }
206
isScheduler(void)207 bool Process::isScheduler(void)
208 {
209 return false;
210 }
211
212 #else
213
214 #ifndef WEXITSTATUS
215 #define WEXITSTATUS(status) ((unsigned)(status) >> 8)
216 #endif
217
218 #ifndef WIFEXITED
219 #define WIFEXITED(status) (((status) & 255) == 0)
220 #endif
221
222 #ifndef WTERMSIG
223 #define WTERMSIG(status) (((unsigned)(status)) & 0x7F)
224 #endif
225
226 #ifndef WIFSIGNALLED
227 #define WIFSIGNALLED(status) (((status) & 255) != 0)
228 #endif
229
230 #ifndef WCOREDUMP
231 #define WCOREDUMP(status) (((status) & 0x80) != 0)
232 #endif
233
lookup(void)234 static void lookup(void)
235 {
236 struct passwd *pw = NULL;
237 #ifdef HAVE_GETPWUID_R
238 struct passwd pwd;
239 char buffer[1024];
240
241 ::getpwuid_r(geteuid(), &pwd, buffer, 1024, &pw);
242 #else
243 pw = ::getpwuid(geteuid());
244 #endif
245
246 if(_pHome)
247 delString(_pHome);
248 if(_pUser)
249 delString(_pUser);
250
251 _pUser = _pHome = NULL;
252
253 if(pw != NULL && pw->pw_dir != NULL)
254 _pHome = newString(pw->pw_dir);
255
256 if(pw != NULL && pw->pw_name != NULL)
257 _pUser = newString(pw->pw_name);
258
259 endpwent();
260 }
261
getUser(void)262 const char *Process::getUser(void)
263 {
264 if(!_pUser)
265 lookup();
266
267 return (const char *)_pUser;
268 }
269
getConfigDir(void)270 const char *Process::getConfigDir(void)
271 {
272 #ifdef ETC_CONFDIR
273 return ETC_CONFDIR;
274 #else
275 return ETC_PREFIX;
276 #endif
277 }
278
getHomeDir(void)279 const char *Process::getHomeDir(void)
280 {
281 if(!_pHome)
282 lookup();
283
284 return (const char *)_pHome;
285 }
286
287 #ifdef HAVE_GETPAGESIZE
getPageSize(void)288 size_t Process::getPageSize(void)
289 {
290 return (size_t)getpagesize();
291 }
292
293 #else
294
getPageSize(void)295 size_t Process::getPageSize(void)
296 {
297 return 1024;
298 }
299
300 #endif
301
setUser(const char * id,bool grp)302 bool Process::setUser(const char *id, bool grp)
303 {
304 struct passwd *pw = NULL;
305 #ifdef HAVE_GETPWNAM_R
306 struct passwd pwd;
307 char buffer[1024];
308
309 ::getpwnam_r(id, &pwd, buffer, 1024, &pw);
310 #else
311 pw = ::getpwnam(id);
312 #endif
313 if(!pw)
314 return false;
315
316 if(grp)
317 if(setgid(pw->pw_gid))
318 return false;
319
320 if(setuid(pw->pw_uid))
321 return false;
322
323 lookup();
324 return true;
325 }
326
setGroup(const char * id)327 bool Process::setGroup(const char *id)
328 {
329 struct group *group = NULL;
330 #ifdef HAVE_GETGRNAM_R
331 struct group grp;
332 char buffer[2048];
333
334 ::getgrnam_r(id, &grp, buffer, 1024, &group);
335 #else
336 group = ::getgrnam(id);
337 #endif
338 if(!group) {
339 endgrent();
340 return false;
341 }
342
343 #ifdef HAVE_SETEGID
344 setegid(group->gr_gid);
345 #endif
346 if(setgid(group->gr_gid)) {
347 endgrent();
348 return false;
349 }
350
351 endgrent();
352 return true;
353 }
354
cancel(int pid,int sig)355 bool Process::cancel(int pid, int sig)
356 {
357 if(!sig)
358 sig = SIGTERM;
359
360 if(pid < 1)
361 return false;
362
363 if(::kill(pid, sig))
364 return false;
365
366 return true;
367 }
368
join(int pid)369 int Process::join(int pid)
370 {
371 int status;
372
373 if(pid < 1)
374 return -1;
375
376 #ifdef HAVE_WAITPID
377 waitpid(pid, &status, 0);
378 #else
379 #ifdef HAVE_WAIT4
380 wait4(pid, &status, 0, NULL);
381 #else
382 int result;
383 while((result = wait(&status)) != pid && result != -1)
384 ;
385 #endif
386 #endif
387
388 if(WIFEXITED(status))
389 return WEXITSTATUS(status);
390 else if(WIFSIGNALLED(status))
391 return -WTERMSIG(status);
392 else
393 return -1;
394 }
395
spawn(const char * exename,const char ** args,bool wait)396 int Process::spawn(const char *exename, const char **args, bool wait)
397 {
398 int pid;
399
400 pid = vfork();
401 if(pid == -1)
402 return -1;
403
404 if(!pid) {
405 execvp((char *)exename, (char **)args);
406 _exit(-1);
407 }
408
409 if(!wait)
410 return pid;
411
412 return join(pid);
413 }
414
setInterruptSignal(int signo,Trap func)415 Process::Trap Process::setInterruptSignal(int signo, Trap func)
416 {
417 struct sigaction sig_act, old_act;
418
419 memset(&sig_act, 0, sizeof(sig_act));
420 sig_act.sa_handler = func;
421 sigemptyset(&sig_act.sa_mask);
422 if(signo != SIGALRM)
423 sigaddset(&sig_act.sa_mask, SIGALRM);
424
425 sig_act.sa_flags = 0;
426 #ifdef SA_INTERRUPT
427 sig_act.sa_flags |= SA_INTERRUPT;
428 #endif
429 if(sigaction(signo, &sig_act, &old_act) < 0)
430 return SIG_ERR;
431
432 return old_act.sa_handler;
433 }
434
setPosixSignal(int signo,Trap func)435 Process::Trap Process::setPosixSignal(int signo, Trap func)
436 {
437 struct sigaction sig_act, old_act;
438
439 memset(&sig_act, 0, sizeof(sig_act));
440 sig_act.sa_handler = func;
441 sigemptyset(&sig_act.sa_mask);
442 sig_act.sa_flags = 0;
443 if(signo == SIGALRM) {
444 #ifdef SA_INTERRUPT
445 sig_act.sa_flags |= SA_INTERRUPT;
446 #endif
447 }
448 else {
449 sigaddset(&sig_act.sa_mask, SIGALRM);
450 #ifdef SA_RESTART
451 sig_act.sa_flags |= SA_RESTART;
452 #endif
453 }
454 if(sigaction(signo, &sig_act, &old_act) < 0)
455 return SIG_ERR;
456 return old_act.sa_handler;
457 }
458
detach(void)459 void Process::detach(void)
460 {
461 attach("/dev/null");
462 }
463
attach(const char * dev)464 void Process::attach(const char *dev)
465 {
466 int pid;
467 int fd;
468
469 if(getppid() == 1)
470 return;
471
472 ::close(0);
473 ::close(1);
474 ::close(2);
475
476 #ifdef SIGTTOU
477 setPosixSignal(SIGTTOU, SIG_IGN);
478 #endif
479
480 #ifdef SIGTTIN
481 setPosixSignal(SIGTTIN, SIG_IGN);
482 #endif
483
484 #ifdef SIGTSTP
485 setPosixSignal(SIGTSTP, SIG_IGN);
486 #endif
487
488 if((pid = fork()) < 0)
489 THROW(pid);
490 else if(pid > 0)
491 exit(0);
492
493
494 #if defined(SIGTSTP) && defined(TIOCNOTTY)
495 if(setpgid(0, getpid()) == -1)
496 THROW(-1);
497
498 if((fd = open(_PATH_TTY, O_RDWR)) >= 0) {
499 ioctl(fd, TIOCNOTTY, NULL);
500 close(fd);
501 }
502 #else
503
504 #ifdef HAVE_SETPGRP
505 if(setpgrp() == -1)
506 THROW(-1);
507 #else
508 if(setpgid(0, getpid()) == -1)
509 THROW(-1);
510 #endif
511
512 setPosixSignal(SIGHUP, SIG_IGN);
513
514 if((pid = fork()) < 0)
515 THROW(-1);
516 else if(pid > 0)
517 exit(0);
518 #endif
519
520 if(dev && *dev) {
521 ::open(dev, O_RDWR);
522 ::open(dev, O_RDWR);
523 ::open(dev, O_RDWR);
524 }
525 }
526
setScheduler(const char * pol)527 void Process::setScheduler(const char *pol)
528 {
529 #ifdef _POSIX_PRIORITY_SCHEDULING
530 struct sched_param p;
531 int policy;
532
533 sched_getparam(0, &p);
534
535 if(pol) {
536 #if defined(SCHED_TS)
537 policy = SCHED_TS;
538 #elif defined(SCHED_OTHER)
539 policy = SCHED_OTHER;
540 #else
541 policy = 0;
542 #endif
543
544 #ifdef SCHED_RR
545 if(!stricmp(pol, "rr"))
546 policy = SCHED_RR;
547 #endif
548 #if !defined(SCHED_RR) && defined(SCHED_FIFO)
549 if(!stricmp(pol, "rr"))
550 policy = SCHED_FIFO;
551 #endif
552 #ifdef SCHED_FIFO
553 if(!stricmp(pol, "fifo")) {
554 rtflag = true;
555 policy = SCHED_FIFO;
556 }
557 #endif
558 #ifdef SCHED_TS
559 if(!stricmp(pol, "ts"))
560 policy = SCHED_TS;
561 #endif
562 #ifdef SCHED_OTHER
563 if(!stricmp(pol, "other"))
564 policy = SCHED_OTHER;
565 #endif
566 }
567 else
568 policy = sched_getscheduler(0);
569
570 int min = sched_get_priority_min(policy);
571 int max = sched_get_priority_max(policy);
572
573 if(p.sched_priority < min)
574 p.sched_priority = min;
575 else if(p.sched_priority > max)
576 p.sched_priority = max;
577
578 sched_setscheduler(0, policy, &p);
579 #endif
580 }
581
582
setPriority(int pri)583 void Process::setPriority(int pri)
584 {
585 #ifdef _POSIX_PRIORITY_SCHEDULING
586 struct sched_param p;
587 int policy = sched_getscheduler(0);
588 int min = sched_get_priority_min(policy);
589 int max = sched_get_priority_max(policy);
590
591 sched_getparam(0, &p);
592
593 if(pri < min)
594 pri = min;
595 if(pri > max)
596 pri = max;
597 p.sched_priority = pri;
598 sched_setparam(0, &p);
599 #else
600 if(pri < -20)
601 pri = -20;
602 if(pri > 20)
603 pri = 20;
604 nice(-pri);
605 #endif
606 }
607
isScheduler(void)608 bool Process::isScheduler(void)
609 {
610 #ifdef _POSIX_PRIORITY_SCHEDULING
611 return true;
612 #else
613 return false;
614 #endif
615 }
616
setRealtime(int pri)617 void Process::setRealtime(int pri)
618 {
619 if(pri < 1)
620 pri = 1;
621
622 setScheduler("rr");
623 setPriority(pri);
624 }
625
626 #endif // not win32
627
628 #ifdef _OSF_SOURCE
629 #undef HAVE_SETENV
630 #endif
631
setEnv(const char * name,const char * value,bool overwrite)632 void Process::setEnv(const char *name, const char *value, bool overwrite)
633 {
634 #ifdef HAVE_SETENV
635 ::setenv(name, value, (int)overwrite);
636 #else
637 char strbuf[256];
638
639 snprintf(strbuf, sizeof(strbuf), "%s=%s", name, value);
640 if(!overwrite)
641 if(getenv(strbuf))
642 return;
643
644 ::putenv(strdup(strbuf));
645 #endif
646 }
647
getEnv(const char * name)648 const char *Process::getEnv(const char *name)
649 {
650 return ::getenv(name);
651 }
652
653 #if defined(HAVE_MLOCKALL) && defined(MCL_FUTURE)
654
655 #include <sys/mman.h>
656
lock(bool future)657 bool Process::lock(bool future)
658 {
659 int rc;
660
661 if(future)
662 rc = mlockall(MCL_CURRENT | MCL_FUTURE);
663 else
664 rc = mlockall(MCL_CURRENT);
665 if(rc)
666 return false;
667
668 return true;
669 }
670
unlock(void)671 void Process::unlock(void)
672 {
673 munlockall();
674 }
675 #else
676
lock(bool future)677 bool Process::lock(bool future)
678 {
679 return false;
680 }
681
unlock(void)682 void Process::unlock(void)
683 {
684 }
685
686 #endif
687
688 #ifdef CCXX_NAMESPACES
689 }
690 #endif
691
692 /** EMACS **
693 * Local variables:
694 * mode: c++
695 * c-basic-offset: 4
696 * End:
697 */
698