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