Merge branch 'redirect-urlauth'
[libi2ncommon] / src / daemonfunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
0a654ec0
TJ
20/***************************************************************************
21 * Copyright (C) 2008 by Intra2net AG - Thomas Jarosch *
0a654ec0
TJ
22 ***************************************************************************/
23#include <sys/types.h>
24#include <unistd.h>
5efd35b1 25#include <stdlib.h>
0a654ec0
TJ
26#include <pwd.h>
27#include <grp.h>
28
29#include <string>
30#include <stdexcept>
1eb904be 31#include "daemonfunc.hpp"
6a93d84a
TJ
32#include "stringfunc.hxx"
33#include "filefunc.hxx"
34
a287a306
TJ
35
36namespace I2n
6a93d84a 37{
1eb904be 38namespace Daemon
6a93d84a 39{
0a654ec0
TJ
40
41using namespace std;
42
43/**
44 * Fork into the background.
45 */
6a93d84a 46void daemonize()
0a654ec0 47{
90246b4a
TJ
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
0a654ec0
TJ
60}
61
62/**
09684efc 63 * Drop root privileges
0a654ec0
TJ
64 * @param username User to become. Don't change user if empty
65 * @param group Group to become. Don't change group if empty
90246b4a
TJ
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
0a654ec0 68 */
90246b4a
TJ
69bool drop_root_privileges(const std::string &username,
70 const std::string &group, bool get_group_from_user)
0a654ec0 71{
90246b4a
TJ
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
dcd37d35 94 // Initialize additional groups the user is a member of
a6238e63 95 if (initgroups(username.c_str(), getgid()) == -1)
dcd37d35
TJ
96 return false;
97
90246b4a
TJ
98 if (setuid(my_user.Uid))
99 return false;
100 }
e43beb91
TJ
101
102 return true;
6a93d84a
TJ
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 */
118bool pid_of(const std::string& name, std::vector< pid_t >& result)
119{
90246b4a
TJ
120 std::vector< std::string > entries;
121 std::vector< pid_t > fuzz1_result;
122 std::vector< pid_t > fuzz2_result;
123 result.clear();
edd749de
GE
124
125 if (name.empty()) return false;
90246b4a 126 if (!get_dir("/proc", entries)) return false;
edd749de 127
90246b4a
TJ
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);
edd749de 139 if (!real_exe.empty())
90246b4a 140 {
edd749de
GE
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 }
90246b4a 158 }
edd749de 159 else
90246b4a 160 {
edd749de
GE
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 }
90246b4a
TJ
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;
6a93d84a
TJ
193} // eo pidOf(const std::string&,std::vector< pid_t >&)
194
195}
0a654ec0 196}