99f3be8fcf1d7812a70da913f2463aae5a989514
[libi2ncommon] / src / daemonfunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /***************************************************************************
21  *   Copyright (C) 2008 by Intra2net AG - Thomas Jarosch                   *
22  ***************************************************************************/
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <pwd.h>
27 #include <grp.h>
28
29 #include <string>
30 #include <stdexcept>
31 #include "daemonfunc.hpp"
32 #include "stringfunc.hxx"
33 #include "filefunc.hxx"
34
35
36 namespace I2n
37 {
38 namespace Daemon
39 {
40
41 using namespace std;
42
43 /**
44  * Fork into the background.
45  */
46 void daemonize()
47 {
48    int pid=fork();
49
50    if (pid < 0)
51    {
52       throw runtime_error("fork() failed");
53    }
54    if (pid > 0)
55    {
56       // parent process
57       exit (0);
58    }
59    // pid==0 -> child process: continue
60 }
61
62 /**
63  * Drop root privileges
64  * @param username User to become. Don't change user if empty
65  * @param group Group to become. Don't change group if empty
66  * @param get_group_from_user Get group GID from user information if group is empty.
67  * @return true if all is fine, false otherwise
68  */
69 bool drop_root_privileges(const std::string &username,
70                           const std::string &group, bool get_group_from_user)
71 {
72    if (!group.empty())
73    {
74       Group my_group(group);
75       if (!my_group.is_valid())
76          return false;
77
78       if (setgid((my_group.Gid)))
79          return false;
80    }
81
82    if (!username.empty())
83    {
84       User my_user(username);
85       if (!my_user.is_valid())
86          return false;
87
88       if (get_group_from_user && group.empty())
89       {
90          if (setgid((my_user.Gid)))
91             return false;
92       }
93
94       // Initialize additional groups the user is a member of
95       if (initgroups(username.c_str(), getgid()) == -1)
96          return false;
97
98       if (setuid(my_user.Uid))
99          return false;
100    }
101
102     return true;
103 }
104
105 /**
106  * @brief determine the pids for a given program
107  * @param[in] name name (or full path) of the binary
108  * @param[out] result the pids associated with the name.
109  * @return @a true if the function performed without errors.
110  *
111  * Walk though the /proc/\<pid\>'s and search for the name.
112  *
113  * @note Since this function uses /proc, it's system specific. Currently:
114  * Linux only!
115  *
116  * @todo check cmdline and stat in /proc/\<pid\> dir for the searched name.
117  */
118 bool pid_of(const std::string& name, std::vector< pid_t >& result)
119 {
120    std::vector< std::string > entries;
121    std::vector< pid_t > fuzz1_result;
122    std::vector< pid_t > fuzz2_result;
123    result.clear();
124
125    if (name.empty()) return false;
126    if (!get_dir("/proc", entries)) return false;
127
128    for (std::vector< std::string >::const_iterator it= entries.begin();
129          it != entries.end();
130          ++it)
131    {
132       pid_t pid;
133       if (! string_to<pid_t>(*it, pid)) continue;
134       std::string base_path= std::string("/proc/") + *it;
135       std::string exe_path= base_path + "/exe";
136       I2n::Stat stat(exe_path, false);
137       if (not stat or not stat.is_link()) continue;
138       std::string real_exe= read_link(exe_path);
139       if (!real_exe.empty())
140       {
141          // we got the path of the exe
142          if (real_exe == name)
143          {
144                result.push_back( pid );
145                continue;
146          }
147
148          std::string proc_stat= read_file( base_path + "/stat");
149          if (proc_stat.empty()) continue; // process vanished
150
151          //TODO some more fuzz tests here?! (cmdline, stat(us))
152
153          if (basename(real_exe) == name)
154          {
155                fuzz2_result.push_back(pid);
156                continue;
157          }
158       }
159       else
160       {
161          // we haven't got the path of the exe
162          // this can happen e.g. with processes owned by other users
163          // -> parse the commandline instead
164          std::string cmdline = read_file(base_path + "/cmdline");
165          if (cmdline.empty()) continue;
166
167          // in /proc/*/cmdline, the parameters are split by nullbytes, we only care for the first parameter
168          if (cmdline.find('\0') != string::npos)
169             cmdline.erase(cmdline.find('\0'));
170
171          if (cmdline == name)
172          {
173                result.push_back( pid );
174                continue;
175          }
176
177          if (basename(cmdline) == name)
178          {
179                fuzz2_result.push_back(pid);
180                continue;
181          }
182       }
183    }
184    if (result.empty())
185    {
186       result.swap(fuzz1_result);
187    }
188    if (result.empty())
189    {
190       result.swap(fuzz2_result);
191    }
192    return true;
193 } // eo pidOf(const std::string&,std::vector< pid_t >&)
194
195 }
196 }