blob: 7b2db074f790551678d847f8b527f7a76d351083 [file] [log] [blame]
Alexandre Lisionddd731e2014-01-31 11:50:08 -05001// 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 <ucommon-config.h>
40#include <commoncpp/config.h>
41#include <commoncpp/export.h>
42#include <commoncpp/thread.h>
43#include <commoncpp/process.h>
44
45#include <cstdlib>
46#include <cstdio>
47#include <cerrno>
48#include <csignal>
49
50#ifdef HAVE_FCNTL_H
51#include <fcntl.h>
52#endif
53
54#ifdef MACOSX
55#undef _POSIX_PRIORITY_SCHEDULING
56#endif
57
58#ifndef _MSWINDOWS_
59#ifdef HAVE_SYS_TIME_H
60#include <sys/time.h>
61#endif
62
63#ifdef HAVE_SYS_WAIT_H
64#include <sys/wait.h>
65#endif
66
67#include <pwd.h>
68#include <grp.h>
69
70#ifdef SIGTSTP
71#include <sys/file.h>
72#include <sys/ioctl.h>
73#endif
74
75#ifndef _PATH_TTY
76#define _PATH_TTY "/dev/tty"
77#endif
78#endif
79
80#ifdef _MSWINDOWS_
81#include <process.h>
82#include <stdio.h>
83#endif
84
85using namespace COMMONCPP_NAMESPACE;
86
87bool Process::rtflag = false;
88
89#ifdef _MSWINDOWS_
90
91static SYSTEM_INFO sysinfo;
92static LPSYSTEM_INFO lpSysInfo = NULL;
93
94static void init_sysinfo(void)
95{
96 if(!lpSysInfo) {
97 lpSysInfo = &sysinfo;
98 memset(&sysinfo, 0, sizeof(sysinfo));
99 GetSystemInfo(lpSysInfo);
100 }
101}
102
103const char *Process::getUser(void)
104{
105 static char userid[65];
106 DWORD length = sizeof(userid);
107
108 if(GetUserName(userid, &length))
109 return userid;
110
111 return NULL;
112}
113
114size_t Process::getPageSize(void)
115{
116 init_sysinfo();
117 return (size_t) lpSysInfo->dwPageSize;
118}
119
120int Process::spawn(const char *exename, const char **args, bool wait)
121{
122 int mode = P_NOWAIT;
123
124 if(wait)
125 mode = P_WAIT;
126
127 return (int)::spawnvp(mode, (char *)exename, (char **)args);
128}
129
130int Process::join(int pid)
131{
132 int status, result;
133
134 if(pid == -1)
135 return pid;
136
137 result = (int)cwait(&status, pid, WAIT_CHILD);
138 if(status & 0x0)
139 return -1;
140 return result;
141}
142
143bool Process::cancel(int pid, int sig)
144{
145 HANDLE hPid = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
146 bool rtn = true;
147
148 if(!hPid)
149 return false;
150
151 switch(sig) {
152 case SIGABRT:
153 case SIGTERM:
154 if(!TerminateProcess(hPid, -1))
155 rtn = false;
156 case 0:
157 break;
158 default:
159 rtn = false;
160 }
161 CloseHandle(hPid);
162 return rtn;
163}
164
165void Process::setPriority(int pri)
166{
167 DWORD pc = NORMAL_PRIORITY_CLASS;
168 DWORD pid = GetCurrentProcessId();
169 HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, TRUE, pid);
170
171#ifdef BELOW_NORMAL_PRIORITY_CLASS
172 if(pri == -1)
173 pc = BELOW_NORMAL_PRIORITY_CLASS;
174 else if(pri == 1)
175 pc = ABOVE_NORMAL_PRIORITY_CLASS;
176 else
177#endif
178 if(pri == 2)
179 pc = HIGH_PRIORITY_CLASS;
180 else if(pri > 2)
181 pc = REALTIME_PRIORITY_CLASS;
182 else if(pri < -1)
183 pc = IDLE_PRIORITY_CLASS;
184
185 SetPriorityClass(hProcess, pc);
186 CloseHandle(hProcess);
187}
188
189void Process::setScheduler(const char *cp)
190{
191 if(!stricmp(cp, "fifo"))
192 setPriority(3);
193 else if(!stricmp(cp, "rr"))
194 setPriority(2);
195 else if(!stricmp(cp, "idle"))
196 setPriority(-2);
197 else
198 setPriority(0);
199}
200
201void Process::setRealtime(int pri)
202{
203 setPriority(3);
204}
205
206bool Process::isScheduler(void)
207{
208 return false;
209}
210
211#else
212
213#ifndef WEXITSTATUS
214#define WEXITSTATUS(status) ((unsigned)(status) >> 8)
215#endif
216
217#ifndef WIFEXITED
218#define WIFEXITED(status) (((status) & 255) == 0)
219#endif
220
221#ifndef WTERMSIG
222#define WTERMSIG(status) (((unsigned)(status)) & 0x7F)
223#endif
224
225#ifndef WIFSIGNALLED
226#define WIFSIGNALLED(status) (((status) & 255) != 0)
227#endif
228
229#ifndef WCOREDUMP
230#define WCOREDUMP(status) (((status) & 0x80) != 0)
231#endif
232
233static char *_pUser = NULL;
234static char *_pHome = NULL;
235
236static void lookup(void)
237{
238 struct passwd *pw = NULL;
239#ifdef HAVE_GETPWUID_R
240 struct passwd pwd;
241 char buffer[1024];
242
243 ::getpwuid_r(geteuid(), &pwd, buffer, 1024, &pw);
244#else
245 pw = ::getpwuid(geteuid());
246#endif
247
248 if(_pHome)
249 delString(_pHome);
250 if(_pUser)
251 delString(_pUser);
252
253 _pUser = _pHome = NULL;
254
255 if(pw != NULL && pw->pw_dir != NULL)
256 _pHome = newString(pw->pw_dir);
257
258 if(pw != NULL && pw->pw_name != NULL)
259 _pUser = newString(pw->pw_name);
260
261 endpwent();
262}
263
264const char *Process::getUser(void)
265{
266 if(!_pUser)
267 lookup();
268
269 return (const char *)_pUser;
270}
271
272const char *Process::getConfigDir(void)
273{
274#ifdef ETC_CONFDIR
275 return ETC_CONFDIR;
276#else
277 return "/etc";
278#endif
279}
280
281const char *Process::getHomeDir(void)
282{
283 if(!_pHome)
284 lookup();
285
286 return (const char *)_pHome;
287}
288
289#ifdef HAVE_GETPAGESIZE
290size_t Process::getPageSize(void)
291{
292 return (size_t)getpagesize();
293}
294
295#else
296
297size_t Process::getPageSize(void)
298{
299 return 1024;
300}
301#endif
302
303bool Process::setUser(const char *id, bool grp)
304{
305 struct passwd *pw = NULL;
306#ifdef HAVE_GETPWNAM_R
307 struct passwd pwd;
308 char buffer[1024];
309
310 ::getpwnam_r(id, &pwd, buffer, 1024, &pw);
311#else
312 pw = ::getpwnam(id);
313#endif
314 if(!pw)
315 return false;
316
317 if(grp)
318 if(setgid(pw->pw_gid))
319 return false;
320
321 if(setuid(pw->pw_uid))
322 return false;
323
324 lookup();
325 return true;
326}
327
328bool Process::setGroup(const char *id)
329{
330 struct group *group = NULL;
331#ifdef HAVE_GETGRNAM_R
332 struct group grp;
333 char buffer[2048];
334
335 ::getgrnam_r(id, &grp, buffer, 1024, &group);
336#else
337 group = ::getgrnam(id);
338#endif
339 if(!group) {
340 //endgrent();
341 return false;
342 }
343
344#ifdef HAVE_SETEGID
345 setegid(group->gr_gid);
346#endif
347 if(setgid(group->gr_gid)) {
348 //endgrent();
349 return false;
350 }
351
352 //endgrent();
353 return true;
354}
355
356bool Process::cancel(int pid, int sig)
357{
358 if(!sig)
359 sig = SIGTERM;
360
361 if(pid < 1)
362 return false;
363
364 if(::kill(pid, sig))
365 return false;
366
367 return true;
368}
369
370int Process::join(int pid)
371{
372 int status;
373
374 if(pid < 1)
375 return -1;
376
377#ifdef HAVE_WAITPID
378 waitpid(pid, &status, 0);
379#else
380#ifdef HAVE_WAIT4
381 wait4(pid, &status, 0, NULL);
382#else
383 int result;
384 while((result = ::wait(&status)) != pid && result != -1)
385 ;
386#endif
387#endif
388
389 if(WIFEXITED(status))
390 return WEXITSTATUS(status);
391 else if(WIFSIGNALLED(status))
392 return -WTERMSIG(status);
393 else
394 return -1;
395}
396
397int Process::spawn(const char *exename, const char **args, bool wait)
398{
399 int pid;
400
401 pid = vfork();
402 if(pid == -1)
403 return -1;
404
405 if(!pid) {
406 execvp((char *)exename, (char **)args);
407 _exit(-1);
408 }
409
410 if(!wait)
411 return pid;
412
413 return join(pid);
414}
415
416Process::Trap Process::setInterruptSignal(int signo, Trap func)
417{
418 struct sigaction sig_act, old_act;
419
420 memset(&sig_act, 0, sizeof(sig_act));
421 sig_act.sa_handler = func;
422 sigemptyset(&sig_act.sa_mask);
423 if(signo != SIGALRM)
424 sigaddset(&sig_act.sa_mask, SIGALRM);
425
426 sig_act.sa_flags = 0;
427#ifdef SA_INTERRUPT
428 sig_act.sa_flags |= SA_INTERRUPT;
429#endif
430 if(sigaction(signo, &sig_act, &old_act) < 0)
431 return SIG_ERR;
432
433 return old_act.sa_handler;
434}
435
436Process::Trap Process::setPosixSignal(int signo, Trap func)
437{
438 struct sigaction sig_act, old_act;
439
440 memset(&sig_act, 0, sizeof(sig_act));
441 sig_act.sa_handler = func;
442 sigemptyset(&sig_act.sa_mask);
443 sig_act.sa_flags = 0;
444 if(signo == SIGALRM) {
445#ifdef SA_INTERRUPT
446 sig_act.sa_flags |= SA_INTERRUPT;
447#endif
448 }
449 else {
450 sigaddset(&sig_act.sa_mask, SIGALRM);
451#ifdef SA_RESTART
452 sig_act.sa_flags |= SA_RESTART;
453#endif
454 }
455 if(sigaction(signo, &sig_act, &old_act) < 0)
456 return SIG_ERR;
457 return old_act.sa_handler;
458}
459
460void Process::detach(void)
461{
462 attach("/dev/null");
463}
464
465void Process::attach(const char *dev)
466{
467 int pid;
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 int fd;
496 if(setpgid(0, getpid()) == -1)
497 THROW(-1);
498
499 if((fd = open(_PATH_TTY, O_RDWR)) >= 0) {
500 ioctl(fd, TIOCNOTTY, NULL);
501 close(fd);
502 }
503#else
504
505#ifdef HAVE_SETPGRP
506 if(setpgrp() == -1)
507 THROW(-1);
508#else
509 if(setpgid(0, getpid()) == -1)
510 THROW(-1);
511#endif
512
513 setPosixSignal(SIGHUP, SIG_IGN);
514
515 if((pid = fork()) < 0)
516 THROW(-1);
517 else if(pid > 0)
518 exit(0);
519#endif
520
521 if(dev && *dev) {
522 ::open(dev, O_RDWR);
523 ::open(dev, O_RDWR);
524 ::open(dev, O_RDWR);
525 }
526}
527
528void Process::setScheduler(const char *pol)
529{
530#ifdef _POSIX_PRIORITY_SCHEDULING
531 struct sched_param p;
532 int policy, orig;
533 pthread_t ptid = pthread_self();
534
535 if(pthread_getschedparam(ptid, &policy, &p))
536 return;
537
538 orig = policy;
539 if(pol) {
540#if defined(SCHED_TS)
541 policy = SCHED_TS;
542#elif defined(SCHED_OTHER)
543 policy = SCHED_OTHER;
544#else
545 policy = 0;
546#endif
547
548#ifdef SCHED_RR
549 if(ucommon::eq_case(pol, "rr"))
550 policy = SCHED_RR;
551#endif
552#if !defined(SCHED_RR) && defined(SCHED_FIFO)
553 if(ucommon::eq_case(pol, "rr"))
554 policy = SCHED_FIFO;
555#endif
556#ifdef SCHED_FIFO
557 if(ucommon::eq_case(pol, "fifo")) {
558 rtflag = true;
559 policy = SCHED_FIFO;
560 }
561#endif
562#ifdef SCHED_TS
563 if(ucommon::eq_case(pol, "ts"))
564 policy = SCHED_TS;
565#endif
566#ifdef SCHED_OTHER
567 if(ucommon::eq_case(pol, "other"))
568 policy = SCHED_OTHER;
569#endif
570 }
571 else
572 policy = orig;
573
574 int min = sched_get_priority_min(policy);
575 int max = sched_get_priority_max(policy);
576
577 if(p.sched_priority < min)
578 p.sched_priority = min;
579 else if(p.sched_priority > max)
580 p.sched_priority = max;
581
582 pthread_setschedparam(ptid, policy, &p);
583#endif
584}
585
586void Process::setPriority(int pri)
587{
588#ifdef _POSIX_PRIORITY_SCHEDULING
589 struct sched_param p;
590 int policy;
591 pthread_t ptid = pthread_self();
592
593 pthread_getschedparam(ptid, &policy, &p);
594
595 int min = sched_get_priority_min(policy);
596 int max = sched_get_priority_max(policy);
597
598 if(pri < min)
599 pri = min;
600 if(pri > max)
601 pri = max;
602 p.sched_priority = pri;
603 pthread_setschedparam(ptid, policy, &p);
604#else
605 if(pri < -20)
606 pri = -20;
607 if(pri > 20)
608 pri = 20;
609 nice(-pri);
610#endif
611}
612
613bool Process::isScheduler(void)
614{
615#ifdef _POSIX_PRIORITY_SCHEDULING
616 return true;
617#else
618 return false;
619#endif
620}
621
622void Process::setRealtime(int pri)
623{
624 if(pri < 1)
625 pri = 1;
626
627 setScheduler("rr");
628 setPriority(pri);
629}
630
631#endif
632
633#ifdef _OSF_SOURCE
634#undef HAVE_SETENV
635#endif
636
637void Process::setEnv(const char *name, const char *value, bool overwrite)
638{
639#ifdef HAVE_SETENV
640 ::setenv(name, value, (int)overwrite);
641#else
642 char strbuf[256];
643
644 snprintf(strbuf, sizeof(strbuf), "%s=%s", name, value);
645 if(!overwrite)
646 if(getenv(strbuf))
647 return;
648
649 ::putenv(strdup(strbuf));
650#endif
651}
652
653const char *Process::getEnv(const char *name)
654{
655 return ::getenv(name);
656}
657
658#if defined(HAVE_MLOCKALL) && defined(MCL_FUTURE)
659
660#include <sys/mman.h>
661
662bool Process::lock(bool future)
663{
664 int rc;
665
666 if(future)
667 rc = mlockall(MCL_CURRENT | MCL_FUTURE);
668 else
669 rc = mlockall(MCL_CURRENT);
670 if(rc)
671 return false;
672
673 return true;
674}
675
676void Process::unlock(void)
677{
678 munlockall();
679}
680#else
681
682bool Process::lock(bool future)
683{
684 return false;
685}
686
687void Process::unlock(void)
688{
689}
690
691#endif
692
693#ifdef _MSWINDOWS_
694
695Lockfile::Lockfile()
696{
697 _mutex = INVALID_HANDLE_VALUE;
698 _flagged = false;
699}
700
701Lockfile::Lockfile(const char *name)
702{
703 _mutex = INVALID_HANDLE_VALUE;
704 _flagged = false;
705 lock(name);
706}
707
708bool Lockfile::lock(const char *name)
709{
710 char mname[65];
711 char *ext = strrchr((char *)name, '/');
712
713 if(ext)
714 name = ++ext;
715
716 unlock();
717 snprintf(mname, sizeof(mname) - 4, "_lock_%s", name);
718 ext = strrchr(mname, '.');
719 if(ext && !stricmp(ext, ".lock")) {
720 *ext = 0;
721 ext = NULL;
722 }
723 if(!ext)
724 addString(mname, sizeof(mname), ".lck");
725 _mutex = CreateMutex(NULL, FALSE, mname);
726 if(WaitForSingleObject(_mutex, 200) == WAIT_OBJECT_0)
727 _flagged = true;
728 return _flagged;
729}
730
731void Lockfile::unlock(void)
732{
733 if(_mutex == INVALID_HANDLE_VALUE)
734 return;
735
736 if(_flagged)
737 ReleaseMutex(_mutex);
738 CloseHandle(_mutex);
739 _flagged = false;
740 _mutex = INVALID_HANDLE_VALUE;
741}
742
743bool Lockfile::isLocked(void)
744{
745 return _flagged;
746}
747
748#else
749Lockfile::Lockfile()
750{
751 _path = NULL;
752}
753
754Lockfile::Lockfile(const char *name)
755{
756 _path = NULL;
757 lock(name);
758}
759
760bool Lockfile::lock(const char *name)
761{
762 struct stat ino;
763 int fd, pid, status;
764 const char *ext;
765 char buffer[128];
766 bool rtn = true;
767
768 unlock();
769
770 ext = strrchr(name, '/');
771 if(ext)
772 ext = strrchr(ext, '.');
773 else
774 ext = strrchr(name, '.');
775
776 if(strchr(name, '/')) {
777 _path = new char[strlen(name) + 1];
778 strcpy(_path, name);
779 }
780 else if(ext && !strcmp(ext, ".pid")) {
781 if(stat("/var/run", &ino))
782 snprintf(buffer, sizeof(buffer), "/tmp/.%s", name);
783 else
784 snprintf(buffer, sizeof(buffer), "/var/run/%s", name);
785 _path = new char[strlen(buffer) + 1];
786 strcpy(_path, buffer);
787 }
788 else {
789 if(!ext)
790 ext = ".lock";
791 if(stat("/var/lock", &ino))
792 snprintf(buffer, sizeof(buffer), "/tmp/.%s%s", name, ext);
793 else
794 snprintf(buffer, sizeof(buffer), "/var/lock/%s%s", name, ext);
795
796 _path = new char[strlen(buffer) + 1];
797 strcpy(_path, buffer);
798 }
799
800 for(;;) {
801 fd = ::open(_path, O_WRONLY | O_CREAT | O_EXCL, 0660);
802 if(fd > 0) {
803 pid = getpid();
804 snprintf(buffer, sizeof(buffer), "%d\n", pid);
805 if(::write(fd, buffer, strlen(buffer)))
806 rtn = false;
807 ::close(fd);
808 return rtn;
809 }
810 if(fd < 0 && errno != EEXIST) {
811 delete[] _path;
812 return false;
813 }
814
815 fd = ::open(_path, O_RDONLY);
816 if(fd < 0) {
817 if(errno == ENOENT)
818 continue;
819 delete[] _path;
820 return false;
821 }
822
823 Thread::sleep(2000);
824 status = ::read(fd, buffer, sizeof(buffer) - 1);
825 if(status < 1) {
826 ::close(fd);
827 continue;
828 }
829
830 buffer[status] = 0;
831 pid = atoi(buffer);
832 if(pid) {
833 if(pid == getpid()) {
834 status = -1;
835 errno = 0;
836 }
837 else
838 status = kill(pid, 0);
839
840 if(!status || (errno == EPERM)) {
841 ::close(fd);
842 delete[] _path;
843 return false;
844 }
845 }
846 ::close(fd);
847 ::unlink(_path);
848 }
849}
850
851void Lockfile::unlock(void)
852{
853 if(_path) {
854 remove(_path);
855 delete[] _path;
856 _path = NULL;
857 }
858}
859
860bool Lockfile::isLocked(void)
861{
862 if(_path)
863 return true;
864
865 return false;
866}
867
868#endif
869
870