1 #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> 8 #include <sys/fcntl.h> 9 #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 */ 21 char umid[UMID_LEN] = { 0 }; 22 23 /* Changed by set_uml_dir and make_uml_dir, which are run early in boot */ 24 static char *uml_dir = UML_DIR; 25 26 static int __init make_uml_dir(void) 27 { 28 char dir[512] = { '\0' }; 29 int len, err; 30 31 if(*uml_dir == '~'){ 32 char *home = getenv("HOME"); 33 34 err = -ENOENT; 35 if(home == NULL){ 36 printk("make_uml_dir : no value in environment for " 37 "$HOME\n"); 38 goto err; 39 } 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 48 err = -ENOMEM; 49 uml_dir = malloc(strlen(dir) + 1); 50 if (uml_dir == NULL) { 51 printf("make_uml_dir : malloc failed, errno = %d\n", errno); 52 goto err; 53 } 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)); 58 err = -errno; 59 goto err_free; 60 } 61 return 0; 62 63 err_free: 64 free(uml_dir); 65 err: 66 uml_dir = NULL; 67 return err; 68 } 69 70 static 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); 78 if(directory == NULL) 79 return -errno; 80 81 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; 85 if(len > sizeof(file)) 86 return -E2BIG; 87 88 sprintf(file, "%s/%s", dir, ent->d_name); 89 if(unlink(file) < 0) 90 return -errno; 91 } 92 if(rmdir(dir) < 0) 93 return -errno; 94 95 return 0; 96 } 97 98 /* 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. 106 */ 107 108 static int not_dead_yet(char *dir) 109 { 110 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; 111 char pid[sizeof("nnnnn\0")], *end; 112 int dead, fd, p, n, err; 113 114 n = snprintf(file, sizeof(file), "%s/pid", dir); 115 if(n >= sizeof(file)){ 116 printk("not_dead_yet - pid filename too long\n"); 117 err = -E2BIG; 118 goto out; 119 } 120 121 dead = 0; 122 fd = open(file, O_RDONLY); 123 if(fd < 0) { 124 fd = -errno; 125 if(fd != -ENOENT){ 126 printk("not_dead_yet : couldn't open pid file '%s', " 127 "err = %d\n", file, -fd); 128 } 129 goto out; 130 } 131 132 err = 0; 133 n = read(fd, pid, sizeof(pid)); 134 if(n < 0){ 135 printk("not_dead_yet : couldn't read pid file '%s', " 136 "err = %d\n", file, errno); 137 goto out_close; 138 } else if(n == 0){ 139 printk("not_dead_yet : couldn't read pid file '%s', " 140 "0-byte read\n", file); 141 goto out_close; 142 } 143 144 p = strtoul(pid, &end, 0); 145 if(end == pid){ 146 printk("not_dead_yet : couldn't parse pid file '%s', " 147 "errno = %d\n", file, errno); 148 goto out_close; 149 } 150 151 if((kill(p, 0) == 0) || (errno != ESRCH)){ 152 printk("umid \"%s\" is already in use by pid %d\n", umid, p); 153 return 1; 154 } 155 156 err = actually_do_remove(dir); 157 if(err) 158 printk("not_dead_yet - actually_do_remove failed with " 159 "err = %d\n", err); 160 161 return err; 162 163 out_close: 164 close(fd); 165 out: 166 return 0; 167 } 168 169 static void __init create_pid_file(void) 170 { 171 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; 172 char pid[sizeof("nnnnn\0")]; 173 int fd, n; 174 175 if(umid_file_name("pid", file, sizeof(file))) 176 return; 177 178 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644); 179 if(fd < 0){ 180 printk("Open of machine pid file \"%s\" failed: %s\n", 181 file, strerror(errno)); 182 return; 183 } 184 185 snprintf(pid, sizeof(pid), "%d\n", getpid()); 186 n = write(fd, pid, strlen(pid)); 187 if(n != strlen(pid)) 188 printk("Write of pid file failed - err = %d\n", errno); 189 190 close(fd); 191 } 192 193 int __init set_umid(char *name) 194 { 195 if(strlen(name) > UMID_LEN - 1) 196 return -E2BIG; 197 198 strlcpy(umid, name, sizeof(umid)); 199 200 return 0; 201 } 202 203 static int umid_setup = 0; 204 205 int __init make_umid(void) 206 { 207 int fd, err; 208 char tmp[256]; 209 210 if(umid_setup) 211 return 0; 212 213 make_uml_dir(); 214 215 if(*umid == '\0'){ 216 strlcpy(tmp, uml_dir, sizeof(tmp)); 217 strlcat(tmp, "XXXXXX", sizeof(tmp)); 218 fd = mkstemp(tmp); 219 if(fd < 0){ 220 printk("make_umid - mkstemp(%s) failed: %s\n", 221 tmp, strerror(errno)); 222 err = -errno; 223 goto err; 224 } 225 226 close(fd); 227 228 set_umid(&tmp[strlen(uml_dir)]); 229 230 /* There's a nice tiny little race between this unlink and 231 * the mkdir below. It'd be nice if there were a mkstemp 232 * for directories. 233 */ 234 if(unlink(tmp)){ 235 err = -errno; 236 goto err; 237 } 238 } 239 240 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid); 241 err = mkdir(tmp, 0777); 242 if(err < 0){ 243 err = -errno; 244 if(err != -EEXIST) 245 goto err; 246 247 /* 1 -> this umid is already in use 248 * < 0 -> we couldn't remove the umid directory 249 * In either case, we can't use this umid, so return -EEXIST. 250 */ 251 if(not_dead_yet(tmp) != 0) 252 goto err; 253 254 err = mkdir(tmp, 0777); 255 } 256 if(err){ 257 err = -errno; 258 printk("Failed to create '%s' - err = %d\n", umid, -errno); 259 goto err; 260 } 261 262 umid_setup = 1; 263 264 create_pid_file(); 265 266 err = 0; 267 err: 268 return err; 269 } 270 271 static int __init make_umid_init(void) 272 { 273 if(!make_umid()) 274 return 0; 275 276 /* If initializing with the given umid failed, then try again with 277 * a random one. 278 */ 279 printk("Failed to initialize umid \"%s\", trying with a random umid\n", 280 umid); 281 *umid = '\0'; 282 make_umid(); 283 284 return 0; 285 } 286 287 __initcall(make_umid_init); 288 289 int __init umid_file_name(char *name, char *buf, int len) 290 { 291 int n, err; 292 293 err = make_umid(); 294 if(err) 295 return err; 296 297 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name); 298 if(n >= len){ 299 printk("umid_file_name : buffer too short\n"); 300 return -E2BIG; 301 } 302 303 return 0; 304 } 305 306 char *get_umid(void) 307 { 308 return umid; 309 } 310 311 static int __init set_uml_dir(char *name, int *add) 312 { 313 if(*name == '\0'){ 314 printf("uml_dir can't be an empty string\n"); 315 return 0; 316 } 317 318 if(name[strlen(name) - 1] == '/'){ 319 uml_dir = name; 320 return 0; 321 } 322 323 uml_dir = malloc(strlen(name) + 2); 324 if(uml_dir == NULL){ 325 printf("Failed to malloc uml_dir - error = %d\n", errno); 326 327 /* Return 0 here because do_initcalls doesn't look at 328 * the return value. 329 */ 330 return 0; 331 } 332 sprintf(uml_dir, "%s/", name); 333 334 return 0; 335 } 336 337 __uml_setup("uml_dir=", set_uml_dir, 338 "uml_dir=<directory>\n" 339 " The location to place the pid and umid files.\n\n" 340 ); 341 342 static void remove_umid_dir(void) 343 { 344 char dir[strlen(uml_dir) + UMID_LEN + 1], err; 345 346 sprintf(dir, "%s%s", uml_dir, umid); 347 err = actually_do_remove(dir); 348 if(err) 349 printf("remove_umid_dir - actually_do_remove failed with " 350 "err = %d\n", err); 351 } 352 353 __uml_exitcall(remove_umid_dir); 354