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