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