1 /* mpdscribble (MPD Client)
2 * Copyright (C) 2008-2010 The Music Player Daemon Project
3 * Copyright (C) 2005-2008 Kuno Woudt <kuno@frob.nl>
4 * Project homepage: http://musicpd.org
5
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "daemon.h"
22
23 #include <glib.h>
24
25 #ifndef G_OS_WIN32
26 #include <stdbool.h>
27 #include <assert.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdlib.h>
36
37 #include <pwd.h>
38 #include <grp.h>
39 #endif
40
41 #ifndef G_OS_WIN32
42
43 /** the Unix user name which MPD runs as */
44 static char *user_name;
45
46 /** the Unix user id which MPD runs as */
47 static uid_t user_uid;
48
49 /** the Unix group id which MPD runs as */
50 static gid_t user_gid;
51
52 /** the absolute path of the pidfile */
53 static char *pidfile;
54
55 #endif
56
57 void
daemonize_close_stdin(void)58 daemonize_close_stdin(void)
59 {
60 #ifndef G_OS_WIN32
61 int fd = open("/dev/null", O_RDONLY);
62
63 if (fd < 0)
64 close(STDIN_FILENO);
65 else if (fd != STDIN_FILENO) {
66 dup2(fd, STDIN_FILENO);
67 close(fd);
68 }
69 #endif
70 }
71
72 void
daemonize_close_stdout_stderr(void)73 daemonize_close_stdout_stderr(void)
74 {
75 #ifndef G_OS_WIN32
76 int fd = open("/dev/null", O_WRONLY);
77
78 if (fd >= 0) {
79 if (fd != STDOUT_FILENO)
80 dup2(fd, STDOUT_FILENO);
81 if (fd != STDERR_FILENO)
82 dup2(fd, STDERR_FILENO);
83 if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
84 close(fd);
85 } else {
86 close(STDOUT_FILENO);
87 close(STDERR_FILENO);
88 }
89 #endif
90 }
91
92 void
daemonize_set_user(void)93 daemonize_set_user(void)
94 {
95 #ifndef G_OS_WIN32
96 if (user_name == NULL)
97 return;
98
99 /* get uid */
100 if (setgid(user_gid) == -1)
101 g_error("cannot setgid for user \"%s\": %s",
102 user_name, g_strerror(errno));
103
104 #ifdef _BSD_SOURCE
105 /* init suplementary groups
106 * (must be done before we change our uid)
107 */
108 if (initgroups(user_name, user_gid) == -1)
109 g_warning("cannot init supplementary groups "
110 "of user \"%s\": %s",
111 user_name, g_strerror(errno));
112 #endif
113
114 /* set uid */
115 if (setuid(user_uid) == -1)
116 g_error("cannot change to uid of user \"%s\": %s",
117 user_name, g_strerror(errno));
118 #endif
119 }
120
121 void
daemonize_detach(void)122 daemonize_detach(void)
123 {
124 #ifndef G_OS_WIN32
125 int ret;
126
127 /* detach from parent process */
128
129 ret = fork();
130 if (ret < 0)
131 g_error("fork() failed: %s", g_strerror(errno));
132
133 if (ret > 0)
134 /* exit the parent process */
135 _exit(EXIT_SUCCESS);
136
137 /* release the current working directory */
138
139 ret = chdir("/");
140 if (ret < 0)
141 g_error("chdir() failed: %s\n", g_strerror(errno));
142
143 /* detach from the current session */
144
145 setsid();
146 #endif
147 }
148
149 void
daemonize_write_pidfile(void)150 daemonize_write_pidfile(void)
151 {
152 #ifndef G_OS_WIN32
153 FILE *file;
154
155 if (pidfile == NULL)
156 return;
157
158 unlink(pidfile);
159
160 file = fopen(pidfile, "w");
161 if (file == NULL)
162 g_error("Failed to create pidfile %s: %s",
163 pidfile, g_strerror(errno));
164
165 fprintf(file, "%d\n", getpid());
166 fclose(file);
167 #endif
168 }
169
170 void
daemonize_init(const char * user,const char * _pidfile)171 daemonize_init(const char *user, const char *_pidfile)
172 {
173 #ifndef G_OS_WIN32
174 if (user != NULL && strcmp(user, g_get_user_name()) != 0) {
175 struct passwd *pwd;
176
177 user_name = g_strdup(user);
178
179 pwd = getpwnam(user_name);
180 if (pwd == NULL)
181 g_error("no such user \"%s\"", user_name);
182
183 user_uid = pwd->pw_uid;
184 user_gid = pwd->pw_gid;
185 }
186
187 pidfile = g_strdup(_pidfile);
188 #else
189 (void)user;
190 (void)_pidfile;
191 #endif
192 }
193
194 void
daemonize_finish(void)195 daemonize_finish(void)
196 {
197 #ifndef G_OS_WIN32
198 if (pidfile != NULL)
199 unlink(pidfile);
200
201 g_free(user_name);
202 g_free(pidfile);
203 #endif
204 }
205