vserver 1.9.3
[linux-2.6.git] / arch / um / kernel / umid.c
1 /* 
2  * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <dirent.h>
12 #include <signal.h>
13 #include <sys/stat.h>
14 #include <sys/param.h>
15 #include "user.h"
16 #include "umid.h"
17 #include "init.h"
18 #include "os.h"
19 #include "user_util.h"
20 #include "choose-mode.h"
21
22 #define UMID_LEN 64
23 #define UML_DIR "~/.uml/"
24
25 /* Changed by set_umid and make_umid, which are run early in boot */
26 static char umid[UMID_LEN] = { 0 };
27
28 /* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
29 static char *uml_dir = UML_DIR;
30
31 /* Changed by set_umid */
32 static int umid_is_random = 1;
33 static int umid_inited = 0;
34
35 static int make_umid(int (*printer)(const char *fmt, ...));
36
37 static int __init set_umid(char *name, int is_random,
38                            int (*printer)(const char *fmt, ...))
39 {
40         if(umid_inited){
41                 (*printer)("Unique machine name can't be set twice\n");
42                 return(-1);
43         }
44
45         if(strlen(name) > UMID_LEN - 1)
46                 (*printer)("Unique machine name is being truncated to %d "
47                            "characters\n", UMID_LEN);
48         strlcpy(umid, name, sizeof(umid));
49
50         umid_is_random = is_random;
51         umid_inited = 1;
52         return 0;
53 }
54
55 static int __init set_umid_arg(char *name, int *add)
56 {
57         return(set_umid(name, 0, printf));
58 }
59
60 __uml_setup("umid=", set_umid_arg,
61 "umid=<name>\n"
62 "    This is used to assign a unique identity to this UML machine and\n"
63 "    is used for naming the pid file and management console socket.\n\n"
64 );
65
66 int __init umid_file_name(char *name, char *buf, int len)
67 {
68         int n;
69
70         if(!umid_inited && make_umid(printk)) return(-1);
71
72         n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1;
73         if(n > len){
74                 printk("umid_file_name : buffer too short\n");
75                 return(-1);
76         }
77
78         sprintf(buf, "%s%s/%s", uml_dir, umid, name);
79         return(0);
80 }
81
82 extern int tracing_pid;
83
84 static int __init create_pid_file(void)
85 {
86         char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
87         char pid[sizeof("nnnnn\0")];
88         int fd, n;
89
90         if(umid_file_name("pid", file, sizeof(file))) return 0;
91
92         fd = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))), 
93                           0644);
94         if(fd < 0){
95                 printf("Open of machine pid file \"%s\" failed - "
96                        "err = %d\n", file, -fd);
97                 return 0;
98         }
99
100         sprintf(pid, "%d\n", os_getpid());
101         n = os_write_file(fd, pid, strlen(pid));
102         if(n != strlen(pid))
103                 printf("Write of pid file failed - err = %d\n", -n);
104         os_close_file(fd);
105         return 0;
106 }
107
108 static int actually_do_remove(char *dir)
109 {
110         DIR *directory;
111         struct dirent *ent;
112         int len;
113         char file[256];
114
115         directory = opendir(dir);
116         if(directory == NULL){
117                 printk("actually_do_remove : couldn't open directory '%s', "
118                        "errno = %d\n", dir, errno);
119                 return(1);
120         }
121         while((ent = readdir(directory)) != NULL){
122                 if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
123                         continue;
124                 len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
125                 if(len > sizeof(file)){
126                         printk("Not deleting '%s' from '%s' - name too long\n",
127                                ent->d_name, dir);
128                         continue;
129                 }
130                 sprintf(file, "%s/%s", dir, ent->d_name);
131                 if(unlink(file) < 0){
132                         printk("actually_do_remove : couldn't remove '%s' "
133                                "from '%s', errno = %d\n", ent->d_name, dir, 
134                                errno);
135                         return(1);
136                 }
137         }
138         if(rmdir(dir) < 0){
139                 printk("actually_do_remove : couldn't rmdir '%s', "
140                        "errno = %d\n", dir, errno);
141                 return(1);
142         }
143         return(0);
144 }
145
146 void remove_umid_dir(void)
147 {
148         char dir[strlen(uml_dir) + UMID_LEN + 1];
149         if(!umid_inited) return;
150
151         sprintf(dir, "%s%s", uml_dir, umid);
152         actually_do_remove(dir);
153 }
154
155 char *get_umid(int only_if_set)
156 {
157         if(only_if_set && umid_is_random) return(NULL);
158         return(umid);
159 }
160
161 int not_dead_yet(char *dir)
162 {
163         char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
164         char pid[sizeof("nnnnn\0")], *end;
165         int dead, fd, p, n;
166
167         sprintf(file, "%s/pid", dir);
168         dead = 0;
169         fd = os_open_file(file, of_read(OPENFLAGS()), 0);
170         if(fd < 0){
171                 if(fd != -ENOENT){
172                         printk("not_dead_yet : couldn't open pid file '%s', "
173                                "err = %d\n", file, -fd);
174                         return(1);
175                 }
176                 dead = 1;
177         }
178         if(fd > 0){
179                 n = os_read_file(fd, pid, sizeof(pid));
180                 if(n < 0){
181                         printk("not_dead_yet : couldn't read pid file '%s', "
182                                "err = %d\n", file, -n);
183                         return(1);
184                 }
185                 p = strtoul(pid, &end, 0);
186                 if(end == pid){
187                         printk("not_dead_yet : couldn't parse pid file '%s', "
188                                "errno = %d\n", file, errno);
189                         dead = 1;
190                 }
191                 if(((kill(p, 0) < 0) && (errno == ESRCH)) ||
192                    (p == CHOOSE_MODE(tracing_pid, os_getpid())))
193                         dead = 1;
194         }
195         if(!dead) return(1);
196         return(actually_do_remove(dir));
197 }
198
199 static int __init set_uml_dir(char *name, int *add)
200 {
201         if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){
202                 uml_dir = malloc(strlen(name) + 2);
203                 if(uml_dir == NULL){
204                         printf("Failed to malloc uml_dir - error = %d\n",
205                                errno);
206                         uml_dir = name;
207                         /* Return 0 here because do_initcalls doesn't look at
208                          * the return value.
209                          */
210                         return(0);
211                 }
212                 sprintf(uml_dir, "%s/", name);
213         }
214         else uml_dir = name;
215         return(0);
216 }
217
218 static int __init make_uml_dir(void)
219 {
220         char dir[MAXPATHLEN + 1] = { '\0' };
221         int len;
222
223         if(*uml_dir == '~'){
224                 char *home = getenv("HOME");
225
226                 if(home == NULL){
227                         printf("make_uml_dir : no value in environment for "
228                                "$HOME\n");
229                         exit(1);
230                 }
231                 strlcpy(dir, home, sizeof(dir));
232                 uml_dir++;
233         }
234         len = strlen(dir);
235         strncat(dir, uml_dir, sizeof(dir) - len);
236         len = strlen(dir);
237         if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){
238                 dir[len] = '/';
239                 dir[len + 1] = '\0';
240         }
241
242         uml_dir = malloc(strlen(dir) + 1);
243         if(uml_dir == NULL){
244                 printf("make_uml_dir : malloc failed, errno = %d\n", errno);
245                 exit(1);
246         }
247         strcpy(uml_dir, dir);
248         
249         if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){
250                 printf("Failed to mkdir %s - errno = %i\n", uml_dir, errno);
251                 return(-1);
252         }
253         return 0;
254 }
255
256 static int __init make_umid(int (*printer)(const char *fmt, ...))
257 {
258         int fd, err;
259         char tmp[strlen(uml_dir) + UMID_LEN + 1];
260
261         strlcpy(tmp, uml_dir, sizeof(tmp));
262
263         if(!umid_inited){
264                 strcat(tmp, "XXXXXX");
265                 fd = mkstemp(tmp);
266                 if(fd < 0){
267                         (*printer)("make_umid - mkstemp failed, errno = %d\n",
268                                    errno);
269                         return(1);
270                 }
271
272                 os_close_file(fd);
273                 /* There's a nice tiny little race between this unlink and
274                  * the mkdir below.  It'd be nice if there were a mkstemp
275                  * for directories.
276                  */
277                 unlink(tmp);
278                 set_umid(&tmp[strlen(uml_dir)], 1, printer);
279         }
280         
281         sprintf(tmp, "%s%s", uml_dir, umid);
282
283         err = mkdir(tmp, 0777);
284         if(err < 0){
285                 if(errno == EEXIST){
286                         if(not_dead_yet(tmp)){
287                                 (*printer)("umid '%s' is in use\n", umid);
288                                 return(-1);
289                         }
290                         err = mkdir(tmp, 0777);
291                 }
292         }
293         if(err < 0){
294                 (*printer)("Failed to create %s - errno = %d\n", umid, errno);
295                 return(-1);
296         }
297
298         return(0);
299 }
300
301 __uml_setup("uml_dir=", set_uml_dir,
302 "uml_dir=<directory>\n"
303 "    The location to place the pid and umid files.\n\n"
304 );
305
306 __uml_postsetup(make_uml_dir);
307
308 static int __init make_umid_setup(void)
309 {
310         return(make_umid(printf));
311 }
312
313 __uml_postsetup(make_umid_setup);
314 __uml_postsetup(create_pid_file);
315
316 /*
317  * Overrides for Emacs so that we follow Linus's tabbing style.
318  * Emacs will notice this stuff at the end of the file and automatically
319  * adjust the settings for this buffer only.  This must remain at the end
320  * of the file.
321  * ---------------------------------------------------------------------------
322  * Local variables:
323  * c-file-style: "linux"
324  * End:
325  */