[PATCH] uml: fix not_dead_yet when directory is in bad state
[linux-2.6/linux-loongson.git] / arch / um / os-Linux / umid.c
blobd5811710126e83df198b7ff81e5cf3dabef6aa10
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"
16 #define UML_DIR "~/.uml/"
18 #define UMID_LEN 64
20 /* Changed by set_umid, which is run early in boot */
21 char umid[UMID_LEN] = { 0 };
23 /* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
24 static char *uml_dir = UML_DIR;
26 static int __init make_uml_dir(void)
28 char dir[512] = { '\0' };
29 int len, err;
31 if(*uml_dir == '~'){
32 char *home = getenv("HOME");
34 err = -ENOENT;
35 if(home == NULL){
36 printk("make_uml_dir : no value in environment for "
37 "$HOME\n");
38 goto err;
40 strlcpy(dir, home, sizeof(dir));
41 uml_dir++;
43 strlcat(dir, uml_dir, sizeof(dir));
44 len = strlen(dir);
45 if (len > 0 && dir[len - 1] != '/')
46 strlcat(dir, "/", sizeof(dir));
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;
54 strcpy(uml_dir, dir);
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;
61 return 0;
63 err_free:
64 free(uml_dir);
65 err:
66 uml_dir = NULL;
67 return err;
70 static int actually_do_remove(char *dir)
72 DIR *directory;
73 struct dirent *ent;
74 int len;
75 char file[256];
77 directory = opendir(dir);
78 if(directory == NULL)
79 return -errno;
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;
88 sprintf(file, "%s/%s", dir, ent->d_name);
89 if(unlink(file) < 0)
90 return -errno;
92 if(rmdir(dir) < 0)
93 return -errno;
95 return 0;
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.
107 * Boolean return: 1 if in use, 0 otherwise.
109 static inline int is_umdir_used(char *dir)
111 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
112 char pid[sizeof("nnnnn\0")], *end;
113 int dead, fd, p, n, err;
115 n = snprintf(file, sizeof(file), "%s/pid", dir);
116 if(n >= sizeof(file)){
117 printk("is_umdir_used - pid filename too long\n");
118 err = -E2BIG;
119 goto out;
122 dead = 0;
123 fd = open(file, O_RDONLY);
124 if(fd < 0) {
125 fd = -errno;
126 if(fd != -ENOENT){
127 printk("is_umdir_used : couldn't open pid file '%s', "
128 "err = %d\n", file, -fd);
130 goto out;
133 err = 0;
134 n = read(fd, pid, sizeof(pid));
135 if(n < 0){
136 printk("is_umdir_used : couldn't read pid file '%s', "
137 "err = %d\n", file, errno);
138 goto out_close;
139 } else if(n == 0){
140 printk("is_umdir_used : couldn't read pid file '%s', "
141 "0-byte read\n", file);
142 goto out_close;
145 p = strtoul(pid, &end, 0);
146 if(end == pid){
147 printk("is_umdir_used : couldn't parse pid file '%s', "
148 "errno = %d\n", file, errno);
149 goto out_close;
152 if((kill(p, 0) == 0) || (errno != ESRCH)){
153 printk("umid \"%s\" is already in use by pid %d\n", umid, p);
154 return 1;
157 out_close:
158 close(fd);
159 out:
160 return 0;
164 * Try to remove the directory @dir unless it's in use.
165 * Precondition: @dir exists.
166 * Returns 0 for success, < 0 for failure in removal or if the directory is in
167 * use.
169 static int umdir_take_if_dead(char *dir)
171 int ret;
172 if (is_umdir_used(dir))
173 return -EEXIST;
175 ret = actually_do_remove(dir);
176 if (ret) {
177 printk("is_umdir_used - actually_do_remove failed with "
178 "err = %d\n", ret);
180 return ret;
183 static void __init create_pid_file(void)
185 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
186 char pid[sizeof("nnnnn\0")];
187 int fd, n;
189 if(umid_file_name("pid", file, sizeof(file)))
190 return;
192 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
193 if(fd < 0){
194 printk("Open of machine pid file \"%s\" failed: %s\n",
195 file, strerror(errno));
196 return;
199 snprintf(pid, sizeof(pid), "%d\n", getpid());
200 n = write(fd, pid, strlen(pid));
201 if(n != strlen(pid))
202 printk("Write of pid file failed - err = %d\n", errno);
204 close(fd);
207 int __init set_umid(char *name)
209 if(strlen(name) > UMID_LEN - 1)
210 return -E2BIG;
212 strlcpy(umid, name, sizeof(umid));
214 return 0;
217 static int umid_setup = 0;
219 int __init make_umid(void)
221 int fd, err;
222 char tmp[256];
224 if(umid_setup)
225 return 0;
227 make_uml_dir();
229 if(*umid == '\0'){
230 strlcpy(tmp, uml_dir, sizeof(tmp));
231 strlcat(tmp, "XXXXXX", sizeof(tmp));
232 fd = mkstemp(tmp);
233 if(fd < 0){
234 printk("make_umid - mkstemp(%s) failed: %s\n",
235 tmp, strerror(errno));
236 err = -errno;
237 goto err;
240 close(fd);
242 set_umid(&tmp[strlen(uml_dir)]);
244 /* There's a nice tiny little race between this unlink and
245 * the mkdir below. It'd be nice if there were a mkstemp
246 * for directories.
248 if(unlink(tmp)){
249 err = -errno;
250 goto err;
254 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
255 err = mkdir(tmp, 0777);
256 if(err < 0){
257 err = -errno;
258 if(err != -EEXIST)
259 goto err;
261 if (umdir_take_if_dead(tmp) < 0)
262 goto err;
264 err = mkdir(tmp, 0777);
266 if(err){
267 err = -errno;
268 printk("Failed to create '%s' - err = %d\n", umid, -errno);
269 goto err;
272 umid_setup = 1;
274 create_pid_file();
276 err = 0;
277 err:
278 return err;
281 static int __init make_umid_init(void)
283 if(!make_umid())
284 return 0;
286 /* If initializing with the given umid failed, then try again with
287 * a random one.
289 printk("Failed to initialize umid \"%s\", trying with a random umid\n",
290 umid);
291 *umid = '\0';
292 make_umid();
294 return 0;
297 __initcall(make_umid_init);
299 int __init umid_file_name(char *name, char *buf, int len)
301 int n, err;
303 err = make_umid();
304 if(err)
305 return err;
307 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
308 if(n >= len){
309 printk("umid_file_name : buffer too short\n");
310 return -E2BIG;
313 return 0;
316 char *get_umid(void)
318 return umid;
321 static int __init set_uml_dir(char *name, int *add)
323 if(*name == '\0'){
324 printf("uml_dir can't be an empty string\n");
325 return 0;
328 if(name[strlen(name) - 1] == '/'){
329 uml_dir = name;
330 return 0;
333 uml_dir = malloc(strlen(name) + 2);
334 if(uml_dir == NULL){
335 printf("Failed to malloc uml_dir - error = %d\n", errno);
337 /* Return 0 here because do_initcalls doesn't look at
338 * the return value.
340 return 0;
342 sprintf(uml_dir, "%s/", name);
344 return 0;
347 __uml_setup("uml_dir=", set_uml_dir,
348 "uml_dir=<directory>\n"
349 " The location to place the pid and umid files.\n\n"
352 static void remove_umid_dir(void)
354 char dir[strlen(uml_dir) + UMID_LEN + 1], err;
356 sprintf(dir, "%s%s", uml_dir, umid);
357 err = actually_do_remove(dir);
358 if(err)
359 printf("remove_umid_dir - actually_do_remove failed with "
360 "err = %d\n", err);
363 __uml_exitcall(remove_umid_dir);