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 if(fd != -ENOENT){ 125 printk("not_dead_yet : couldn't open pid file '%s', " 126 "err = %d\n", file, -fd); 127 } 128 goto out; 129 } 130 131 err = 0; 132 n = read(fd, pid, sizeof(pid)); 133 if(n <= 0){ 134 printk("not_dead_yet : couldn't read pid file '%s', " 135 "err = %d\n", file, -n); 136 goto out_close; 137 } 138 139 p = strtoul(pid, &end, 0); 140 if(end == pid){ 141 printk("not_dead_yet : couldn't parse pid file '%s', " 142 "errno = %d\n", file, errno); 143 goto out_close; 144 } 145 146 if((kill(p, 0) == 0) || (errno != ESRCH)){ 147 printk("umid \"%s\" is already in use by pid %d\n", umid, p); 148 return 1; 149 } 150 151 err = actually_do_remove(dir); 152 if(err) 153 printk("not_dead_yet - actually_do_remove failed with " 154 "err = %d\n", err); 155 156 return err; 157 158 out_close: 159 close(fd); 160 out: 161 return 0; 162 } 163 164 static void __init create_pid_file(void) 165 { 166 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; 167 char pid[sizeof("nnnnn\0")]; 168 int fd, n; 169 170 if(umid_file_name("pid", file, sizeof(file))) 171 return; 172 173 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644); 174 if(fd < 0){ 175 printk("Open of machine pid file \"%s\" failed: %s\n", 176 file, strerror(-fd)); 177 return; 178 } 179 180 snprintf(pid, sizeof(pid), "%d\n", getpid()); 181 n = write(fd, pid, strlen(pid)); 182 if(n != strlen(pid)) 183 printk("Write of pid file failed - err = %d\n", -n); 184 185 close(fd); 186 } 187 188 int __init set_umid(char *name) 189 { 190 if(strlen(name) > UMID_LEN - 1) 191 return -E2BIG; 192 193 strlcpy(umid, name, sizeof(umid)); 194 195 return 0; 196 } 197 198 static int umid_setup = 0; 199 200 int __init make_umid(void) 201 { 202 int fd, err; 203 char tmp[256]; 204 205 if(umid_setup) 206 return 0; 207 208 make_uml_dir(); 209 210 if(*umid == '\0'){ 211 strlcpy(tmp, uml_dir, sizeof(tmp)); 212 strlcat(tmp, "XXXXXX", sizeof(tmp)); 213 fd = mkstemp(tmp); 214 if(fd < 0){ 215 printk("make_umid - mkstemp(%s) failed: %s\n", 216 tmp, strerror(errno)); 217 err = -errno; 218 goto err; 219 } 220 221 close(fd); 222 223 set_umid(&tmp[strlen(uml_dir)]); 224 225 /* There's a nice tiny little race between this unlink and 226 * the mkdir below. It'd be nice if there were a mkstemp 227 * for directories. 228 */ 229 if(unlink(tmp)){ 230 err = -errno; 231 goto err; 232 } 233 } 234 235 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid); 236 err = mkdir(tmp, 0777); 237 if(err < 0){ 238 err = -errno; 239 if(err != -EEXIST) 240 goto err; 241 242 /* 1 -> this umid is already in use 243 * < 0 -> we couldn't remove the umid directory 244 * In either case, we can't use this umid, so return -EEXIST. 245 */ 246 if(not_dead_yet(tmp) != 0) 247 goto err; 248 249 err = mkdir(tmp, 0777); 250 } 251 if(err){ 252 err = -errno; 253 printk("Failed to create '%s' - err = %d\n", umid, -errno); 254 goto err; 255 } 256 257 umid_setup = 1; 258 259 create_pid_file(); 260 261 err = 0; 262 err: 263 return err; 264 } 265 266 static int __init make_umid_init(void) 267 { 268 if(!make_umid()) 269 return 0; 270 271 /* If initializing with the given umid failed, then try again with 272 * a random one. 273 */ 274 printk("Failed to initialize umid \"%s\", trying with a random umid\n", 275 umid); 276 *umid = '\0'; 277 make_umid(); 278 279 return 0; 280 } 281 282 __initcall(make_umid_init); 283 284 int __init umid_file_name(char *name, char *buf, int len) 285 { 286 int n, err; 287 288 err = make_umid(); 289 if(err) 290 return err; 291 292 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name); 293 if(n >= len){ 294 printk("umid_file_name : buffer too short\n"); 295 return -E2BIG; 296 } 297 298 return 0; 299 } 300 301 char *get_umid(void) 302 { 303 return umid; 304 } 305 306 static int __init set_uml_dir(char *name, int *add) 307 { 308 if(*name == '\0'){ 309 printf("uml_dir can't be an empty string\n"); 310 return 0; 311 } 312 313 if(name[strlen(name) - 1] == '/'){ 314 uml_dir = name; 315 return 0; 316 } 317 318 uml_dir = malloc(strlen(name) + 2); 319 if(uml_dir == NULL){ 320 printf("Failed to malloc uml_dir - error = %d\n", errno); 321 322 /* Return 0 here because do_initcalls doesn't look at 323 * the return value. 324 */ 325 return 0; 326 } 327 sprintf(uml_dir, "%s/", name); 328 329 return 0; 330 } 331 332 __uml_setup("uml_dir=", set_uml_dir, 333 "uml_dir=<directory>\n" 334 " The location to place the pid and umid files.\n\n" 335 ); 336 337 static void remove_umid_dir(void) 338 { 339 char dir[strlen(uml_dir) + UMID_LEN + 1], err; 340 341 sprintf(dir, "%s%s", uml_dir, umid); 342 err = actually_do_remove(dir); 343 if(err) 344 printf("remove_umid_dir - actually_do_remove failed with " 345 "err = %d\n", err); 346 } 347 348 __uml_exitcall(remove_umid_dir); 349