blob: d5811710126e83df198b7ff81e5cf3dabef6aa10 [file] [log] [blame]
Jeff Dike2264c472006-01-06 00:18:59 -08001#include <stdio.h>
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5#include <errno.h>
6#include <signal.h>
7#include <dirent.h>
Jeff Dike7eebe8a2006-01-06 00:19:01 -08008#include <sys/fcntl.h>
Jeff Dike2264c472006-01-06 00:18:59 -08009#include <sys/stat.h>
10#include <sys/param.h>
11#include "init.h"
12#include "os.h"
13#include "user.h"
14#include "mode.h"
15
16#define UML_DIR "~/.uml/"
17
18#define UMID_LEN 64
19
20/* Changed by set_umid, which is run early in boot */
21char umid[UMID_LEN] = { 0 };
22
23/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
24static char *uml_dir = UML_DIR;
25
26static int __init make_uml_dir(void)
27{
28 char dir[512] = { '\0' };
Jeff Dike7eebe8a2006-01-06 00:19:01 -080029 int len, err;
Jeff Dike2264c472006-01-06 00:18:59 -080030
31 if(*uml_dir == '~'){
32 char *home = getenv("HOME");
33
Jeff Dike7eebe8a2006-01-06 00:19:01 -080034 err = -ENOENT;
Jeff Dike2264c472006-01-06 00:18:59 -080035 if(home == NULL){
Jeff Dike7eebe8a2006-01-06 00:19:01 -080036 printk("make_uml_dir : no value in environment for "
Jeff Dike2264c472006-01-06 00:18:59 -080037 "$HOME\n");
Jeff Dike7eebe8a2006-01-06 00:19:01 -080038 goto err;
Jeff Dike2264c472006-01-06 00:18:59 -080039 }
40 strlcpy(dir, home, sizeof(dir));
41 uml_dir++;
42 }
43 strlcat(dir, uml_dir, sizeof(dir));
44 len = strlen(dir);
45 if (len > 0 && dir[len - 1] != '/')
46 strlcat(dir, "/", sizeof(dir));
47
Jeff Dike7eebe8a2006-01-06 00:19:01 -080048 err = -ENOMEM;
Jeff Dike2264c472006-01-06 00:18:59 -080049 uml_dir = malloc(strlen(dir) + 1);
50 if (uml_dir == NULL) {
51 printf("make_uml_dir : malloc failed, errno = %d\n", errno);
Jeff Dike7eebe8a2006-01-06 00:19:01 -080052 goto err;
Jeff Dike2264c472006-01-06 00:18:59 -080053 }
54 strcpy(uml_dir, dir);
55
56 if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){
57 printf("Failed to mkdir '%s': %s\n", uml_dir, strerror(errno));
Jeff Dike7eebe8a2006-01-06 00:19:01 -080058 err = -errno;
59 goto err_free;
Jeff Dike2264c472006-01-06 00:18:59 -080060 }
61 return 0;
Jeff Dike7eebe8a2006-01-06 00:19:01 -080062
63err_free:
64 free(uml_dir);
65err:
66 uml_dir = NULL;
67 return err;
Jeff Dike2264c472006-01-06 00:18:59 -080068}
69
70static int actually_do_remove(char *dir)
71{
72 DIR *directory;
73 struct dirent *ent;
74 int len;
75 char file[256];
76
77 directory = opendir(dir);
Jeff Dike7eebe8a2006-01-06 00:19:01 -080078 if(directory == NULL)
79 return -errno;
80
Jeff Dike2264c472006-01-06 00:18:59 -080081 while((ent = readdir(directory)) != NULL){
82 if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
83 continue;
84 len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
Jeff Dike7eebe8a2006-01-06 00:19:01 -080085 if(len > sizeof(file))
86 return -E2BIG;
87
Jeff Dike2264c472006-01-06 00:18:59 -080088 sprintf(file, "%s/%s", dir, ent->d_name);
Jeff Dike7eebe8a2006-01-06 00:19:01 -080089 if(unlink(file) < 0)
90 return -errno;
Jeff Dike2264c472006-01-06 00:18:59 -080091 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -080092 if(rmdir(dir) < 0)
93 return -errno;
94
95 return 0;
Jeff Dike2264c472006-01-06 00:18:59 -080096}
97
Jeff Dike7eebe8a2006-01-06 00:19:01 -080098/* This says that there isn't already a user of the specified directory even if
99 * there are errors during the checking. This is because if these errors
100 * happen, the directory is unusable by the pre-existing UML, so we might as
101 * well take it over. This could happen either by
102 * the existing UML somehow corrupting its umid directory
103 * something other than UML sticking stuff in the directory
104 * this boot racing with a shutdown of the other UML
105 * In any of these cases, the directory isn't useful for anything else.
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700106 *
107 * Boolean return: 1 if in use, 0 otherwise.
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800108 */
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700109static inline int is_umdir_used(char *dir)
Jeff Dike2264c472006-01-06 00:18:59 -0800110{
111 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
112 char pid[sizeof("nnnnn\0")], *end;
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800113 int dead, fd, p, n, err;
Jeff Dike2264c472006-01-06 00:18:59 -0800114
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800115 n = snprintf(file, sizeof(file), "%s/pid", dir);
116 if(n >= sizeof(file)){
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700117 printk("is_umdir_used - pid filename too long\n");
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800118 err = -E2BIG;
119 goto out;
120 }
121
Jeff Dike2264c472006-01-06 00:18:59 -0800122 dead = 0;
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800123 fd = open(file, O_RDONLY);
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700124 if(fd < 0) {
125 fd = -errno;
Jeff Dike2264c472006-01-06 00:18:59 -0800126 if(fd != -ENOENT){
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700127 printk("is_umdir_used : couldn't open pid file '%s', "
Jeff Dike2264c472006-01-06 00:18:59 -0800128 "err = %d\n", file, -fd);
Jeff Dike2264c472006-01-06 00:18:59 -0800129 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800130 goto out;
Jeff Dike2264c472006-01-06 00:18:59 -0800131 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800132
133 err = 0;
134 n = read(fd, pid, sizeof(pid));
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700135 if(n < 0){
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700136 printk("is_umdir_used : couldn't read pid file '%s', "
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700137 "err = %d\n", file, errno);
138 goto out_close;
139 } else if(n == 0){
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700140 printk("is_umdir_used : couldn't read pid file '%s', "
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700141 "0-byte read\n", file);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800142 goto out_close;
Jeff Dike2264c472006-01-06 00:18:59 -0800143 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800144
145 p = strtoul(pid, &end, 0);
146 if(end == pid){
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700147 printk("is_umdir_used : couldn't parse pid file '%s', "
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800148 "errno = %d\n", file, errno);
149 goto out_close;
150 }
151
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800152 if((kill(p, 0) == 0) || (errno != ESRCH)){
153 printk("umid \"%s\" is already in use by pid %d\n", umid, p);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800154 return 1;
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800155 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800156
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700157out_close:
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800158 close(fd);
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700159out:
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800160 return 0;
Jeff Dike2264c472006-01-06 00:18:59 -0800161}
162
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700163/*
164 * Try to remove the directory @dir unless it's in use.
165 * Precondition: @dir exists.
166 * Returns 0 for success, < 0 for failure in removal or if the directory is in
167 * use.
168 */
169static int umdir_take_if_dead(char *dir)
170{
171 int ret;
172 if (is_umdir_used(dir))
173 return -EEXIST;
174
175 ret = actually_do_remove(dir);
176 if (ret) {
177 printk("is_umdir_used - actually_do_remove failed with "
178 "err = %d\n", ret);
179 }
180 return ret;
181}
182
Jeff Dike2264c472006-01-06 00:18:59 -0800183static void __init create_pid_file(void)
184{
185 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
186 char pid[sizeof("nnnnn\0")];
187 int fd, n;
188
189 if(umid_file_name("pid", file, sizeof(file)))
190 return;
191
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800192 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
Jeff Dike2264c472006-01-06 00:18:59 -0800193 if(fd < 0){
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800194 printk("Open of machine pid file \"%s\" failed: %s\n",
Jeff Dike2ace87b2006-05-01 12:16:00 -0700195 file, strerror(errno));
Jeff Dike2264c472006-01-06 00:18:59 -0800196 return;
197 }
198
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800199 snprintf(pid, sizeof(pid), "%d\n", getpid());
200 n = write(fd, pid, strlen(pid));
Jeff Dike2264c472006-01-06 00:18:59 -0800201 if(n != strlen(pid))
Jeff Dike2ace87b2006-05-01 12:16:00 -0700202 printk("Write of pid file failed - err = %d\n", errno);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800203
204 close(fd);
Jeff Dike2264c472006-01-06 00:18:59 -0800205}
206
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800207int __init set_umid(char *name)
Jeff Dike2264c472006-01-06 00:18:59 -0800208{
209 if(strlen(name) > UMID_LEN - 1)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800210 return -E2BIG;
211
Jeff Dike2264c472006-01-06 00:18:59 -0800212 strlcpy(umid, name, sizeof(umid));
213
214 return 0;
215}
216
217static int umid_setup = 0;
218
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800219int __init make_umid(void)
Jeff Dike2264c472006-01-06 00:18:59 -0800220{
221 int fd, err;
222 char tmp[256];
223
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800224 if(umid_setup)
225 return 0;
226
Jeff Dike2264c472006-01-06 00:18:59 -0800227 make_uml_dir();
228
229 if(*umid == '\0'){
230 strlcpy(tmp, uml_dir, sizeof(tmp));
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800231 strlcat(tmp, "XXXXXX", sizeof(tmp));
Jeff Dike2264c472006-01-06 00:18:59 -0800232 fd = mkstemp(tmp);
233 if(fd < 0){
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800234 printk("make_umid - mkstemp(%s) failed: %s\n",
235 tmp, strerror(errno));
236 err = -errno;
237 goto err;
Jeff Dike2264c472006-01-06 00:18:59 -0800238 }
239
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800240 close(fd);
241
242 set_umid(&tmp[strlen(uml_dir)]);
243
Jeff Dike2264c472006-01-06 00:18:59 -0800244 /* There's a nice tiny little race between this unlink and
245 * the mkdir below. It'd be nice if there were a mkstemp
246 * for directories.
247 */
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800248 if(unlink(tmp)){
249 err = -errno;
250 goto err;
Jeff Dike2264c472006-01-06 00:18:59 -0800251 }
252 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800253
254 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
255 err = mkdir(tmp, 0777);
Jeff Dike2264c472006-01-06 00:18:59 -0800256 if(err < 0){
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800257 err = -errno;
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800258 if(err != -EEXIST)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800259 goto err;
260
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700261 if (umdir_take_if_dead(tmp) < 0)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800262 goto err;
263
264 err = mkdir(tmp, 0777);
265 }
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800266 if(err){
267 err = -errno;
268 printk("Failed to create '%s' - err = %d\n", umid, -errno);
269 goto err;
Jeff Dike2264c472006-01-06 00:18:59 -0800270 }
271
272 umid_setup = 1;
273
274 create_pid_file();
275
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800276 err = 0;
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800277 err:
278 return err;
Jeff Dike2264c472006-01-06 00:18:59 -0800279}
280
281static int __init make_umid_init(void)
282{
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800283 if(!make_umid())
284 return 0;
285
286 /* If initializing with the given umid failed, then try again with
287 * a random one.
288 */
289 printk("Failed to initialize umid \"%s\", trying with a random umid\n",
290 umid);
291 *umid = '\0';
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800292 make_umid();
Jeff Dike2264c472006-01-06 00:18:59 -0800293
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800294 return 0;
Jeff Dike2264c472006-01-06 00:18:59 -0800295}
296
297__initcall(make_umid_init);
298
299int __init umid_file_name(char *name, char *buf, int len)
300{
301 int n, err;
302
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800303 err = make_umid();
304 if(err)
305 return err;
Jeff Dike2264c472006-01-06 00:18:59 -0800306
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800307 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
308 if(n >= len){
Jeff Dike2264c472006-01-06 00:18:59 -0800309 printk("umid_file_name : buffer too short\n");
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800310 return -E2BIG;
Jeff Dike2264c472006-01-06 00:18:59 -0800311 }
312
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800313 return 0;
Jeff Dike2264c472006-01-06 00:18:59 -0800314}
315
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800316char *get_umid(void)
Jeff Dike2264c472006-01-06 00:18:59 -0800317{
Jeff Dike2264c472006-01-06 00:18:59 -0800318 return umid;
319}
320
321static int __init set_uml_dir(char *name, int *add)
322{
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800323 if(*name == '\0'){
324 printf("uml_dir can't be an empty string\n");
325 return 0;
Jeff Dike2264c472006-01-06 00:18:59 -0800326 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800327
328 if(name[strlen(name) - 1] == '/'){
329 uml_dir = name;
330 return 0;
331 }
332
333 uml_dir = malloc(strlen(name) + 2);
334 if(uml_dir == NULL){
335 printf("Failed to malloc uml_dir - error = %d\n", errno);
336
337 /* Return 0 here because do_initcalls doesn't look at
338 * the return value.
339 */
340 return 0;
341 }
342 sprintf(uml_dir, "%s/", name);
343
344 return 0;
Jeff Dike2264c472006-01-06 00:18:59 -0800345}
346
347__uml_setup("uml_dir=", set_uml_dir,
348"uml_dir=<directory>\n"
349" The location to place the pid and umid files.\n\n"
350);
351
352static void remove_umid_dir(void)
353{
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800354 char dir[strlen(uml_dir) + UMID_LEN + 1], err;
Jeff Dike2264c472006-01-06 00:18:59 -0800355
356 sprintf(dir, "%s%s", uml_dir, umid);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800357 err = actually_do_remove(dir);
358 if(err)
359 printf("remove_umid_dir - actually_do_remove failed with "
360 "err = %d\n", err);
Jeff Dike2264c472006-01-06 00:18:59 -0800361}
362
363__uml_exitcall(remove_umid_dir);