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