blob: 10951a9f03b9dcfffed222f9578caa38f7c6ab68 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// 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
82namespace ost {
83#endif
84
85static char *_pUser = NULL;
86static char *_pHome = NULL;
87
88bool Process::rtflag = false;
89
90#ifdef WIN32
91
92static SYSTEM_INFO sysinfo;
93static LPSYSTEM_INFO lpSysInfo = NULL;
94
95static void init_sysinfo(void)
96{
97 if(!lpSysInfo) {
98 lpSysInfo = &sysinfo;
99 memset(&sysinfo, 0, sizeof(sysinfo));
100 GetSystemInfo(lpSysInfo);
101 }
102}
103
104const 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
115size_t Process::getPageSize(void)
116{
117 init_sysinfo();
118 return (size_t) lpSysInfo->dwPageSize;
119}
120
121int 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
131int 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
144bool 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
166void 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
190void 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
202void Process::setRealtime(int pri)
203{
204 setPriority(3);
205}
206
207bool 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
234static 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
262const char *Process::getUser(void)
263{
264 if(!_pUser)
265 lookup();
266
267 return (const char *)_pUser;
268}
269
270const char *Process::getConfigDir(void)
271{
272#ifdef ETC_CONFDIR
273 return ETC_CONFDIR;
274#else
275 return ETC_PREFIX;
276#endif
277}
278
279const char *Process::getHomeDir(void)
280{
281 if(!_pHome)
282 lookup();
283
284 return (const char *)_pHome;
285}
286
287#ifdef HAVE_GETPAGESIZE
288size_t Process::getPageSize(void)
289{
290 return (size_t)getpagesize();
291}
292
293#else
294
295size_t Process::getPageSize(void)
296{
297 return 1024;
298}
299
300#endif
301
302bool 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
327bool 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
355bool 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
369int 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
396int 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
415Process::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
435Process::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
459void Process::detach(void)
460{
461 attach("/dev/null");
462}
463
464void 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
527void 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
583void 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
608bool Process::isScheduler(void)
609{
610#ifdef _POSIX_PRIORITY_SCHEDULING
611 return true;
612#else
613 return false;
614#endif
615}
616
617void 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
632void 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
648const 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
657bool 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
671void Process::unlock(void)
672{
673 munlockall();
674}
675#else
676
677bool Process::lock(bool future)
678{
679 return false;
680}
681
682void 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 */