1 /* $NetBSD: xenbackendd.c,v 1.1.1.1 2008/08/07 20:26:57 cegger Exp $ */
2 /*
3  * Copyright (C) 2006 Manuel Bouyer <bouyer@netbsd.org>
4  * Copyright (C) 2009 Christoph Egger <Christoph.Egger@amd.com>
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; under version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <syslog.h>
29 
30 #include <xenstore.h>
31 
32 #include "_paths.h"
33 
34 #define DEVTYPE_UNKNOWN 0
35 #define DEVTYPE_VIF 1
36 #define DEVTYPE_VBD 2
37 #define DISABLE_EXEC "libxl/disable_udev"
38 
39 #define DOMAIN_PATH "/local/domain/0"
40 
41 #ifndef XEN_SCRIPT_DIR
42 #error XEN_SCRIPT_DIR not defined
43 #endif
44 
45 #ifndef VBD_SCRIPT
46 #define VBD_SCRIPT XEN_SCRIPT_DIR"/block"
47 #endif
48 #ifndef LOG_FILE
49 #define LOG_FILE XEN_LOG_DIR "xenbackendd.log"
50 #endif
51 #ifndef PID_FILE
52 #define PID_FILE XEN_RUN_DIR "xenbackendd.pid"
53 #endif
54 
55 
56 struct xs_handle *xs;
57 
58 int fflag = 0;
59 int dflag = 0;
60 
61 const char *vbd_script = NULL;
62 const char *log_file = NULL;
63 const char *pidfile = NULL;
64 
65 static void
dolog(int pri,const char * fmt,...)66 dolog(int pri, const char *fmt, ...)
67 {
68 	va_list ap;
69 	va_start(ap, fmt);
70 	vfprintf(stderr, fmt, ap);
71 	va_end(ap);
72 	fprintf(stderr, "\n");
73 	fflush(stderr);
74 	va_start(ap, fmt);
75 	vsyslog(pri, fmt, ap);
76 	va_end(ap);
77 }
78 
79 static void
dodebug(const char * fmt,...)80 dodebug(const char *fmt, ...)
81 {
82 	va_list ap;
83 
84 	if (dflag == 0)
85 		return;
86 	va_start(ap, fmt);
87 	vfprintf(stdout, fmt, ap);
88 	va_end(ap);
89 	printf("\n");
90 	fflush(stdout);
91 }
92 
93 static void
doexec(const char * cmd,const char * arg1,const char * arg2)94 doexec(const char *cmd, const char *arg1, const char *arg2)
95 {
96 	dodebug("exec %s %s %s", cmd, arg1, arg2);
97 	switch(vfork()) {
98 	case -1:
99 		dolog(LOG_ERR, "can't vfork: %s", strerror(errno));
100 		break;
101 	case 0:
102 		execl(cmd, cmd, arg1, arg2, NULL);
103 		dolog(LOG_ERR, "can't exec %s: %s", cmd, strerror(errno));
104 		exit(EXIT_FAILURE);
105 		/* NOTREACHED */
106 		break;
107 	default:
108 		wait(NULL);
109 		break;
110 	}
111 }
112 
113 static void
usage(void)114 usage(void)
115 {
116 	fprintf(stderr,
117 	    "usage: %s [-d] [-f] [-l log_file] [-p pif_file] [-s vbd_script]\n",
118 	    getprogname());
119 	exit(EXIT_FAILURE);
120 }
121 
122 static int
xen_setup(void)123 xen_setup(void)
124 {
125 	xs = xs_daemon_open();
126 	if (xs == NULL) {
127 		dolog(LOG_ERR,
128 		    "Failed to contact xenstore (%s).  Is it running?",
129 		    strerror(errno));
130 		goto out;
131 	}
132 
133 	if (!xs_watch(xs, DOMAIN_PATH, "backend")) {
134 		dolog(LOG_ERR, "xenstore watch on backend fails.");
135 		goto out;
136 	}
137 	return 0;
138 
139  out:
140 	if (xs) {
141 		xs_daemon_close(xs);
142 		xs = NULL;
143 	}
144 	return -1;
145 }
146 
147 int
main(int argc,char * const argv[])148 main(int argc, char * const argv[])
149 {
150 	char **vec;
151 	unsigned int num;
152 	char *s;
153 	int state;
154 	char *sstate, *sdisable;
155 	char *p;
156 	char buf[80];
157 	int type;
158 	int ch;
159 	int debug_fd;
160 	FILE *pidfile_f;
161 
162 	while ((ch = getopt(argc, argv, "dfl:p:s:")) != -1) {
163 		switch (ch) {
164 		case 'd':
165 			dflag = 1;
166 			break;
167 		case 'f':
168 			fflag = 1;
169 			break;
170 		case 'l':
171 			log_file = optarg;
172 			break;
173 		case 'p':
174 			pidfile = optarg;
175 		case 's':
176 			vbd_script = optarg;
177 			break;
178 		default:
179 			usage();
180 		}
181 	}
182 
183 	if (vbd_script == NULL)
184 		vbd_script = VBD_SCRIPT;
185 	if (pidfile == NULL)
186 		pidfile = PID_FILE;
187 	if (log_file == NULL)
188 		log_file = LOG_FILE;
189 
190 	openlog("xenbackendd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
191 
192 	if (fflag == 0) {
193 		/* open log file */
194 		debug_fd = open(log_file, O_RDWR | O_CREAT | O_TRUNC, 0644);
195 		if (debug_fd == -1) {
196 			dolog(LOG_ERR, "can't open %s: %s",
197 			    log_file, strerror(errno));
198 			exit(EXIT_FAILURE);
199 		}
200 	}
201 
202 	if (fflag == 0) {
203 		/* daemonize */
204 		pidfile_f = fopen(pidfile, "w");
205 		if (pidfile_f == NULL) {
206 			dolog(LOG_ERR, "can't open %s: %s",
207 			    pidfile, strerror(errno));
208 			exit(EXIT_FAILURE);
209 		}
210 		if (daemon(0, 0) < 0) {
211 			dolog(LOG_ERR, "can't daemonize: %s",
212 			    strerror(errno));
213 			exit(EXIT_FAILURE);
214 		}
215 		fprintf(pidfile_f, "%d\n", (int)getpid());
216 		fclose(pidfile_f);
217 
218 		/* redirect stderr to log file */
219 		if (dup2(debug_fd, STDERR_FILENO) < 0) {
220 			dolog(LOG_ERR, "can't redirect stderr to %s: %s\n",
221 			    log_file, strerror(errno));
222 			exit(EXIT_FAILURE);
223 		}
224 
225 		/* also redirect stdout if we're in debug mode */
226 		if (dflag) {
227 			if (dup2(debug_fd, STDOUT_FILENO) < 0) {
228 				dolog(LOG_ERR,
229 				    "can't redirect stdout to %s: %s\n",
230 				    log_file, strerror(errno));
231 				exit(EXIT_FAILURE);
232 			}
233 		}
234 
235 		close(debug_fd);
236 		debug_fd = -1;
237 	}
238 
239 	if (xen_setup() < 0)
240 		exit(EXIT_FAILURE);
241 
242 	for (;;) {
243 		vec = xs_read_watch(xs, &num);
244 		dodebug("read from xen watch: %s", *vec);
245 		if (!vec) {
246 			dolog(LOG_ERR, "xs_read_watch: NULL\n");
247 			continue;
248 		}
249 
250 		sdisable = xs_read(xs, XBT_NULL, DISABLE_EXEC, 0);
251 		if (sdisable)
252 			goto next1;
253 
254 		if (strlen(vec[XS_WATCH_PATH]) < sizeof("state"))
255 			goto next1;
256 
257 		/* find last component of path, check if it's "state" */
258 		p = &vec[XS_WATCH_PATH][
259 		    strlen(vec[XS_WATCH_PATH]) - sizeof("state")];
260 		if (p[0] != '/')
261 			goto next1;
262 		p[0] = '\0';
263 		p++;
264 		if (strcmp(p, "state") != 0)
265 			goto next1;
266 
267 		snprintf(buf, sizeof(buf), "%s/state", vec[XS_WATCH_PATH]);
268 		sstate = xs_read(xs, XBT_NULL, buf, 0);
269 		if (sstate == NULL) {
270 			dolog(LOG_ERR,
271 			    "Failed to read %s (%s)", buf, strerror(errno));
272 			goto next1;
273 		}
274 
275 		state = atoi(sstate);
276 		snprintf(buf, sizeof(buf), "%s/hotplug-status",
277 		    vec[XS_WATCH_PATH]);
278 		s = xs_read(xs, XBT_NULL, buf, 0);
279 		if (s != NULL && state != 6 /* XenbusStateClosed */)
280 			goto next2;
281 
282 		type = DEVTYPE_UNKNOWN;
283 		if (strncmp(vec[XS_WATCH_PATH],
284 		    DOMAIN_PATH "/backend/vif",
285 		    strlen(DOMAIN_PATH "/backend/vif")) == 0)
286 			type = DEVTYPE_VIF;
287 
288 		if (strncmp(vec[XS_WATCH_PATH],
289 		    DOMAIN_PATH "/backend/vbd",
290 		    strlen(DOMAIN_PATH "/backend/vbd")) == 0)
291 			type = DEVTYPE_VBD;
292 
293 		switch(type) {
294 		case DEVTYPE_VIF:
295 			free(s);
296 			snprintf(buf, sizeof(buf), "%s/script",
297 			    vec[XS_WATCH_PATH]);
298 			s = xs_read(xs, XBT_NULL, buf, 0);
299 			if (s == NULL) {
300 				dolog(LOG_ERR,
301 				    "Failed to read %s (%s)", buf,
302 				    strerror(errno));
303 				goto next2;
304 			}
305 			doexec(s, vec[XS_WATCH_PATH], sstate);
306 			break;
307 
308 		case DEVTYPE_VBD:
309 			doexec(vbd_script, vec[XS_WATCH_PATH], sstate);
310 			break;
311 
312 		default:
313 			break;
314 		}
315 
316 next2:
317 		free(s);
318 		free(sstate);
319 
320 next1:
321 		free(sdisable);
322 		free(vec);
323 	}
324 
325 	return 0;
326 }
327