645fbfe51050d1237d533f5fdf042490ea7a58fa
[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 %s "
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) + 1);
203                 if(uml_dir == NULL){
204                         printf("Failed to malloc uml_dir - error = %d\n",
205                                errno);
206                         uml_dir = name;
207                         return(0);
208                 }
209                 sprintf(uml_dir, "%s/", name);
210         }
211         else uml_dir = name;
212         return 0;
213 }
214
215 static int __init make_uml_dir(void)
216 {
217         char dir[MAXPATHLEN + 1] = { '\0' };
218         int len;
219
220         if(*uml_dir == '~'){
221                 char *home = getenv("HOME");
222
223                 if(home == NULL){
224                         printf("make_uml_dir : no value in environment for "
225                                "$HOME\n");
226                         exit(1);
227                 }
228                 strlcpy(dir, home, sizeof(dir));
229                 uml_dir++;
230         }
231         len = strlen(dir);
232         strncat(dir, uml_dir, sizeof(dir) - len);
233         len = strlen(dir);
234         if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){
235                 dir[len] = '/';
236                 dir[len + 1] = '\0';
237         }
238
239         uml_dir = malloc(strlen(dir) + 1);
240         if(uml_dir == NULL){
241                 printf("make_uml_dir : malloc failed, errno = %d\n", errno);
242                 exit(1);
243         }
244         strcpy(uml_dir, dir);
245         
246         if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){
247                 printf("Failed to mkdir %s - errno = %i\n", uml_dir, errno);
248                 return(-1);
249         }
250         return 0;
251 }
252
253 static int __init make_umid(int (*printer)(const char *fmt, ...))
254 {
255         int fd, err;
256         char tmp[strlen(uml_dir) + UMID_LEN + 1];
257
258         strlcpy(tmp, uml_dir, sizeof(tmp));
259
260         if(!umid_inited){
261                 strcat(tmp, "XXXXXX");
262                 fd = mkstemp(tmp);
263                 if(fd < 0){
264                         (*printer)("make_umid - mkstemp failed, errno = %d\n",
265                                    errno);
266                         return(1);
267                 }
268
269                 os_close_file(fd);
270                 /* There's a nice tiny little race between this unlink and
271                  * the mkdir below.  It'd be nice if there were a mkstemp
272                  * for directories.
273                  */
274                 unlink(tmp);
275                 set_umid(&tmp[strlen(uml_dir)], 1, printer);
276         }
277         
278         sprintf(tmp, "%s%s", uml_dir, umid);
279
280         err = mkdir(tmp, 0777);
281         if(err < 0){
282                 if(errno == EEXIST){
283                         if(not_dead_yet(tmp)){
284                                 (*printer)("umid '%s' is in use\n", umid);
285                                 return(-1);
286                         }
287                         err = mkdir(tmp, 0777);
288                 }
289         }
290         if(err < 0){
291                 (*printer)("Failed to create %s - errno = %d\n", umid, errno);
292                 return(-1);
293         }
294
295         return(0);
296 }
297
298 __uml_setup("uml_dir=", set_uml_dir,
299 "uml_dir=<directory>\n"
300 "    The location to place the pid and umid files.\n\n"
301 );
302
303 __uml_postsetup(make_uml_dir);
304
305 static int __init make_umid_setup(void)
306 {
307         return(make_umid(printf));
308 }
309
310 __uml_postsetup(make_umid_setup);
311 __uml_postsetup(create_pid_file);
312
313 /*
314  * Overrides for Emacs so that we follow Linus's tabbing style.
315  * Emacs will notice this stuff at the end of the file and automatically
316  * adjust the settings for this buffer only.  This must remain at the end
317  * of the file.
318  * ---------------------------------------------------------------------------
319  * Local variables:
320  * c-file-style: "linux"
321  * End:
322  */