1 /* 2 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Licensed under the GPL 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <dirent.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <signal.h> 12 #include <string.h> 13 #include <unistd.h> 14 #include <sys/stat.h> 15 #include "init.h" 16 #include "kern_constants.h" 17 #include "os.h" 18 #include "user.h" 19 20 #define UML_DIR "~/.uml/" 21 22 #define UMID_LEN 64 23 24 /* Changed by set_umid, which is run early in boot */ 25 static char umid[UMID_LEN] = { 0 }; 26 27 /* Changed by set_uml_dir and make_uml_dir, which are run early in boot */ 28 static char *uml_dir = UML_DIR; 29 30 static int __init make_uml_dir(void) 31 { 32 char dir[512] = { '\0' }; 33 int len, err; 34 35 if (*uml_dir == '~') { 36 char *home = getenv("HOME"); 37 38 err = -ENOENT; 39 if (home == NULL) { 40 printk(UM_KERN_ERR "make_uml_dir : no value in " 41 "environment for $HOME\n"); 42 goto err; 43 } 44 strlcpy(dir, home, sizeof(dir)); 45 uml_dir++; 46 } 47 strlcat(dir, uml_dir, sizeof(dir)); 48 len = strlen(dir); 49 if (len > 0 && dir[len - 1] != '/') 50 strlcat(dir, "/", sizeof(dir)); 51 52 err = -ENOMEM; 53 uml_dir = malloc(strlen(dir) + 1); 54 if (uml_dir == NULL) { 55 printf("make_uml_dir : malloc failed, errno = %d\n", errno); 56 goto err; 57 } 58 strcpy(uml_dir, dir); 59 60 if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) { 61 printf("Failed to mkdir '%s': %s\n", uml_dir, strerror(errno)); 62 err = -errno; 63 goto err_free; 64 } 65 return 0; 66 67 err_free: 68 free(uml_dir); 69 err: 70 uml_dir = NULL; 71 return err; 72 } 73 74 /* 75 * Unlinks the files contained in @dir and then removes @dir. 76 * Doesn't handle directory trees, so it's not like rm -rf, but almost such. We 77 * ignore ENOENT errors for anything (they happen, strangely enough - possibly 78 * due to races between multiple dying UML threads). 79 */ 80 static int remove_files_and_dir(char *dir) 81 { 82 DIR *directory; 83 struct dirent *ent; 84 int len; 85 char file[256]; 86 int ret; 87 88 directory = opendir(dir); 89 if (directory == NULL) { 90 if (errno != ENOENT) 91 return -errno; 92 else 93 return 0; 94 } 95 96 while ((ent = readdir(directory)) != NULL) { 97 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) 98 continue; 99 len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1; 100 if (len > sizeof(file)) { 101 ret = -E2BIG; 102 goto out; 103 } 104 105 sprintf(file, "%s/%s", dir, ent->d_name); 106 if (unlink(file) < 0 && errno != ENOENT) { 107 ret = -errno; 108 goto out; 109 } 110 } 111 112 if (rmdir(dir) < 0 && errno != ENOENT) { 113 ret = -errno; 114 goto out; 115 } 116 117 ret = 0; 118 out: 119 closedir(directory); 120 return ret; 121 } 122 123 /* 124 * This says that there isn't already a user of the specified directory even if 125 * there are errors during the checking. This is because if these errors 126 * happen, the directory is unusable by the pre-existing UML, so we might as 127 * well take it over. This could happen either by 128 * the existing UML somehow corrupting its umid directory 129 * something other than UML sticking stuff in the directory 130 * this boot racing with a shutdown of the other UML 131 * In any of these cases, the directory isn't useful for anything else. 132 * 133 * Boolean return: 1 if in use, 0 otherwise. 134 */ 135 static inline int is_umdir_used(char *dir) 136 { 137 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; 138 char pid[sizeof("nnnnn\0")], *end; 139 int dead, fd, p, n, err; 140 141 n = snprintf(file, sizeof(file), "%s/pid", dir); 142 if (n >= sizeof(file)) { 143 printk(UM_KERN_ERR "is_umdir_used - pid filename too long\n"); 144 err = -E2BIG; 145 goto out; 146 } 147 148 dead = 0; 149 fd = open(file, O_RDONLY); 150 if (fd < 0) { 151 fd = -errno; 152 if (fd != -ENOENT) { 153 printk(UM_KERN_ERR "is_umdir_used : couldn't open pid " 154 "file '%s', err = %d\n", file, -fd); 155 } 156 goto out; 157 } 158 159 err = 0; 160 n = read(fd, pid, sizeof(pid)); 161 if (n < 0) { 162 printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file " 163 "'%s', err = %d\n", file, errno); 164 goto out_close; 165 } else if (n == 0) { 166 printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file " 167 "'%s', 0-byte read\n", file); 168 goto out_close; 169 } 170 171 p = strtoul(pid, &end, 0); 172 if (end == pid) { 173 printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file " 174 "'%s', errno = %d\n", file, errno); 175 goto out_close; 176 } 177 178 if ((kill(p, 0) == 0) || (errno != ESRCH)) { 179 printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n", 180 umid, p); 181 return 1; 182 } 183 184 out_close: 185 close(fd); 186 out: 187 return 0; 188 } 189 190 /* 191 * Try to remove the directory @dir unless it's in use. 192 * Precondition: @dir exists. 193 * Returns 0 for success, < 0 for failure in removal or if the directory is in 194 * use. 195 */ 196 static int umdir_take_if_dead(char *dir) 197 { 198 int ret; 199 if (is_umdir_used(dir)) 200 return -EEXIST; 201 202 ret = remove_files_and_dir(dir); 203 if (ret) { 204 printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir " 205 "failed with err = %d\n", ret); 206 } 207 return ret; 208 } 209 210 static void __init create_pid_file(void) 211 { 212 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; 213 char pid[sizeof("nnnnn\0")]; 214 int fd, n; 215 216 if (umid_file_name("pid", file, sizeof(file))) 217 return; 218 219 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644); 220 if (fd < 0) { 221 printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: " 222 "%s\n", file, strerror(errno)); 223 return; 224 } 225 226 snprintf(pid, sizeof(pid), "%d\n", getpid()); 227 n = write(fd, pid, strlen(pid)); 228 if (n != strlen(pid)) 229 printk(UM_KERN_ERR "Write of pid file failed - err = %d\n", 230 errno); 231 232 close(fd); 233 } 234 235 int __init set_umid(char *name) 236 { 237 if (strlen(name) > UMID_LEN - 1) 238 return -E2BIG; 239 240 strlcpy(umid, name, sizeof(umid)); 241 242 return 0; 243 } 244 245 /* Changed in make_umid, which is called during early boot */ 246 static int umid_setup = 0; 247 248 static int __init make_umid(void) 249 { 250 int fd, err; 251 char tmp[256]; 252 253 if (umid_setup) 254 return 0; 255 256 make_uml_dir(); 257 258 if (*umid == '\0') { 259 strlcpy(tmp, uml_dir, sizeof(tmp)); 260 strlcat(tmp, "XXXXXX", sizeof(tmp)); 261 fd = mkstemp(tmp); 262 if (fd < 0) { 263 printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: " 264 "%s\n", tmp, strerror(errno)); 265 err = -errno; 266 goto err; 267 } 268 269 close(fd); 270 271 set_umid(&tmp[strlen(uml_dir)]); 272 273 /* 274 * There's a nice tiny little race between this unlink and 275 * the mkdir below. It'd be nice if there were a mkstemp 276 * for directories. 277 */ 278 if (unlink(tmp)) { 279 err = -errno; 280 goto err; 281 } 282 } 283 284 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid); 285 err = mkdir(tmp, 0777); 286 if (err < 0) { 287 err = -errno; 288 if (err != -EEXIST) 289 goto err; 290 291 if (umdir_take_if_dead(tmp) < 0) 292 goto err; 293 294 err = mkdir(tmp, 0777); 295 } 296 if (err) { 297 err = -errno; 298 printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid, 299 errno); 300 goto err; 301 } 302 303 umid_setup = 1; 304 305 create_pid_file(); 306 307 err = 0; 308 err: 309 return err; 310 } 311 312 static int __init make_umid_init(void) 313 { 314 if (!make_umid()) 315 return 0; 316 317 /* 318 * If initializing with the given umid failed, then try again with 319 * a random one. 320 */ 321 printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a " 322 "random umid\n", umid); 323 *umid = '\0'; 324 make_umid(); 325 326 return 0; 327 } 328 329 __initcall(make_umid_init); 330 331 int __init umid_file_name(char *name, char *buf, int len) 332 { 333 int n, err; 334 335 err = make_umid(); 336 if (err) 337 return err; 338 339 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name); 340 if (n >= len) { 341 printk(UM_KERN_ERR "umid_file_name : buffer too short\n"); 342 return -E2BIG; 343 } 344 345 return 0; 346 } 347 348 char *get_umid(void) 349 { 350 return umid; 351 } 352 353 static int __init set_uml_dir(char *name, int *add) 354 { 355 if (*name == '\0') { 356 printf("uml_dir can't be an empty string\n"); 357 return 0; 358 } 359 360 if (name[strlen(name) - 1] == '/') { 361 uml_dir = name; 362 return 0; 363 } 364 365 uml_dir = malloc(strlen(name) + 2); 366 if (uml_dir == NULL) { 367 printf("Failed to malloc uml_dir - error = %d\n", errno); 368 369 /* 370 * Return 0 here because do_initcalls doesn't look at 371 * the return value. 372 */ 373 return 0; 374 } 375 sprintf(uml_dir, "%s/", name); 376 377 return 0; 378 } 379 380 __uml_setup("uml_dir=", set_uml_dir, 381 "uml_dir=<directory>\n" 382 " The location to place the pid and umid files.\n\n" 383 ); 384 385 static void remove_umid_dir(void) 386 { 387 char dir[strlen(uml_dir) + UMID_LEN + 1], err; 388 389 sprintf(dir, "%s%s", uml_dir, umid); 390 err = remove_files_and_dir(dir); 391 if (err) 392 printf("remove_umid_dir - remove_files_and_dir failed with " 393 "err = %d\n", err); 394 } 395 396 __uml_exitcall(remove_umid_dir); 397