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