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