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