| /* |
| * Copyright (C) 2000-2009, Parallels, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| #include <sys/stat.h> |
| |
| #include "types.h" |
| #include "util.h" |
| #include "list.h" |
| #include "logger.h" |
| #include "vzerror.h" |
| #include "util.h" |
| #include "script.h" |
| #include "fs.h" |
| |
| volatile sig_atomic_t alarm_flag; |
| static char *envp_bash[] = {"HOME=/", "TERM=linux", ENV_PATH, NULL}; |
| |
| int read_script(const char *fname, char *include, char **buf) |
| { |
| struct stat st; |
| char *tmp, *p = NULL; |
| int fd, len = 0; |
| char *inc; |
| |
| if (!fname) { |
| logger(-1, 0, "read_script: file name not specified"); |
| return -1; |
| } |
| /* Read include file first */ |
| if (include != NULL) { |
| inc = vz_malloc(strlen(fname) + strlen(include) + 1); |
| if (!inc) |
| return -1; |
| if ((p = strrchr(fname, '/')) != NULL) { |
| snprintf(inc, p - fname + 2, "%s", fname); |
| strcat(inc, include); |
| } else { |
| snprintf(inc, sizeof(inc), "%s", include); |
| } |
| if (stat_file(inc)) |
| len = read_script(inc, NULL, buf); |
| free(inc); |
| if (len < 0) |
| return -1; |
| } |
| if (stat(fname, &st)) { |
| logger(-1, 0, "file %s not found", fname); |
| return -1; |
| } |
| if ((fd = open(fname, O_RDONLY)) < 0) { |
| logger(-1, errno, "Unable to open %s", fname); |
| goto err; |
| } |
| if (*buf != NULL) { |
| tmp = realloc(*buf, st.st_size + len + 2); |
| if (tmp == NULL) |
| goto err; |
| *buf = tmp; |
| p = *buf + len; |
| } else { |
| *buf = malloc(st.st_size + 2); |
| if (*buf == NULL) |
| goto err; |
| p = *buf; |
| } |
| if ((len = read(fd, p, st.st_size)) < 0) { |
| logger(-1, errno, "Error reading %s", fname); |
| goto err; |
| } |
| p += len; |
| p[0] = '\n'; |
| p[1] = 0; |
| close(fd); |
| |
| return len; |
| err: |
| if (fd > 0) |
| close(fd); |
| free(*buf); |
| return -1; |
| } |
| |
| #define ENV_SIZE 256 |
| int run_script(const char *f, char *argv[], char *env[], int quiet) |
| { |
| int child, pid, fd; |
| int status; |
| int ret, i, j; |
| char *cmd; |
| struct sigaction act, actold; |
| int out[2]; |
| char *envp[ENV_SIZE]; |
| |
| if (!stat_file(f)) { |
| logger(-1, 0, "File %s not found", f); |
| return VZ_NOSCRIPT; |
| } |
| sigaction(SIGCHLD, NULL, &actold); |
| sigemptyset(&act.sa_mask); |
| act.sa_handler = SIG_DFL; |
| act.sa_flags = SA_NOCLDSTOP; |
| sigaction(SIGCHLD, &act, NULL); |
| |
| cmd = arg2str(argv); |
| if (cmd != NULL) { |
| logger(2, 0, "Running: %s", cmd); |
| free(cmd); |
| } |
| if (quiet && pipe(out) < 0) { |
| logger(-1, errno, "run_script: unable to create pipe"); |
| ret = VZ_RESOURCE_ERROR; |
| goto err; |
| } |
| i = 0; |
| for (i = 0; i < ENV_SIZE - 1 && envp_bash[i] != NULL; i++) |
| envp[i] = envp_bash[i]; |
| if (env != NULL) |
| for (j = 0; i < ENV_SIZE - 1 && env[j] != NULL; i++, j++) |
| envp[i] = env[j]; |
| envp[i] = NULL; |
| if ((child = fork()) == 0) { |
| fd = open("/dev/null", O_WRONLY); |
| if (fd < 0) |
| close(0); |
| else |
| dup2(fd, 0); |
| |
| if (quiet) { |
| dup2(fd, 1); |
| dup2(fd, 2); |
| } else { |
| /* |
| dup2(out[1], STDOUT_FILENO); |
| dup2(out[1], STDERR_FILENO); |
| close(out[0]); |
| close(out[1]); |
| */ |
| } |
| execve(f, argv, envp); |
| logger(-1, errno, "Error exec %s", f); |
| exit(1); |
| } else if (child == -1) { |
| logger(-1, errno, "Unable to fork"); |
| ret = VZ_RESOURCE_ERROR; |
| goto err; |
| } |
| while ((pid = waitpid(child, &status, 0)) == -1) |
| if (errno != EINTR) |
| break; |
| ret = VZ_SYSTEM_ERROR; |
| if (pid == child) { |
| if (WIFEXITED(status)) |
| ret = WEXITSTATUS(status); |
| else if (WIFSIGNALED(status)) |
| logger(-1, 0, "Received signal:" |
| " %d in %s", WTERMSIG(status), f); |
| } else { |
| logger(-1, errno, "Error in waitpid"); |
| } |
| err: |
| sigaction(SIGCHLD, &actold, NULL); |
| |
| return ret; |
| } |
| |
| int run_pre_script(int veid, char *script) |
| { |
| char *arg[2]; |
| char *env[4]; |
| char buf[STR_SIZE]; |
| int ret; |
| |
| if (!stat_file(script)) |
| return 0; |
| /* cmd parameters */ |
| arg[0] = script; |
| arg[1] = NULL; |
| /* environment parameters*/ |
| snprintf(buf, sizeof(buf), "VEID=%d", veid); |
| env[0] = strdup(buf); |
| snprintf(buf, sizeof(buf), "VE_CONFFILE=%s%d.conf", VPS_CONF_DIR, veid); |
| env[1] = strdup(buf); |
| env[2] = strdup(ENV_PATH); |
| env[3] = NULL; |
| |
| if ((ret = run_script(script, arg, env, 0))) |
| ret = VZ_ACTIONSCRIPT_ERROR; |
| free_arg(env); |
| return ret; |
| } |
| |
| static int mk_quota_dev(const char *name, dev_t dev) |
| { |
| mode_t mode = S_IFBLK | S_IXGRP; |
| static char *dirs[] = {"/dev", |
| "/etc/udev/devices", |
| "/lib/udev/devices"}; |
| char fname[256]; |
| const char *p; |
| unsigned int i; |
| int ret = 0; |
| |
| if ((p = strrchr(name, '/')) == NULL) |
| p = name; |
| |
| for(i = 0; i < ARRAY_SIZE(dirs); i++) { |
| if (stat_file(dirs[i]) != 1) |
| continue; |
| |
| snprintf(fname, sizeof(fname), "%s/%s", dirs[i], p); |
| unlink(fname); |
| if (mknod(fname, mode, dev)) { |
| logger(-1, errno, "Unable to create %s", fname); |
| ret = 1; |
| } |
| } |
| |
| return ret; |
| } |
| |
| #define PROC_QUOTA "/proc/vz/vzaquota/" |
| #define QUOTA_U "/aquota.user" |
| #define QUOTA_G "/aquota.group" |
| static int mk_vzquota_link() |
| { |
| struct stat st; |
| const char *fs; |
| char buf[64]; |
| |
| if (stat("/", &st)) { |
| logger(-1, errno, "Unable to stat /"); |
| return -1; |
| } |
| fs = vz_fs_get_name(); |
| /* make dev */ |
| snprintf(buf, sizeof(buf), "/dev/%s", fs); |
| mk_quota_dev(buf, st.st_dev); |
| |
| snprintf(buf, sizeof(buf), PROC_QUOTA "%08lx" QUOTA_U, |
| (unsigned long)st.st_dev); |
| unlink(QUOTA_U); |
| if (symlink(buf, QUOTA_U)) |
| logger(-1, errno, "Unable to create symlink %s", buf); |
| snprintf(buf, sizeof(buf), PROC_QUOTA "%08lx" QUOTA_G, |
| (unsigned long)st.st_dev); |
| unlink(QUOTA_G); |
| if (symlink(buf, QUOTA_G)) |
| logger(-1, errno, "Unable to create symlink %s", buf); |
| return 0; |
| } |
| |
| int setup_env_quota(const struct setup_env_quota_param *param) |
| { |
| if (param == NULL) |
| return -1; |
| if (param->dev_name[0] == '\0') /* simfs/vzquota */ |
| return mk_vzquota_link(); |
| /* ploop */ |
| if (mk_quota_dev(param->dev_name, param->dev)) |
| return -1; |
| return system("quotaon -a"); |
| } |
| |
| #define INITTAB_FILE "/etc/inittab" |
| #define INITTAB_VZID "vz:" |
| #define INITTAB_ACTION INITTAB_VZID "2345:once:touch " VZFIFO_FILE "\n" |
| |
| #define EVENTS_DIR "/etc/event.d/" |
| #define EVENTS_FILE EVENTS_DIR "vz_init_done" |
| #define EVENTS_SCRIPT \ |
| "# This task runs if default runlevel is reached\n" \ |
| "# Added by OpenVZ vzctl\n" \ |
| "start on stopped rc2\n" \ |
| "start on stopped rc3\n" \ |
| "start on stopped rc4\n" \ |
| "start on stopped rc5\n" \ |
| "exec touch " VZFIFO_FILE "\n" |
| |
| /* Ubuntu 9.10 needs different machinery */ |
| #define EVENTS_DIR_UBUNTU "/etc/init/" |
| #define EVENTS_FILE_UBUNTU EVENTS_DIR_UBUNTU "vz_init_done.conf" |
| #define EVENTS_SCRIPT_UBUNTU \ |
| "# tell vzctl that start was successfull\n" \ |
| "#\n" \ |
| "# This task is to tell vzctl that start was successfull\n" \ |
| "\n" \ |
| "description \"tell vzctl that start was successfull\"\n" \ |
| "\n" \ |
| "start on stopped rc RUNLEVEL=[2345]\n" \ |
| "\n" \ |
| "task\n" \ |
| "\n" \ |
| "exec touch " VZFIFO_FILE "\n" |
| |
| #define MAX_WAIT_TIMEOUT 60 * 60 |
| |
| int add_reach_runlevel_mark() |
| { |
| int fd, found, ret; |
| ssize_t len, w; |
| char buf[4096]; |
| struct stat st; |
| |
| unlink(VZFIFO_FILE); |
| if (mkfifo(VZFIFO_FILE, 0644)) { |
| fprintf(stderr, "Unable to create " VZFIFO_FILE " %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| /* Create upstart specific script */ |
| if (!stat(EVENTS_DIR_UBUNTU, &st)) { |
| fd = open(EVENTS_FILE_UBUNTU, O_WRONLY|O_TRUNC|O_CREAT, 0644); |
| if (fd == -1) { |
| fprintf(stderr, "Unable to create " |
| EVENTS_FILE_UBUNTU ": %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| len = sizeof(EVENTS_SCRIPT_UBUNTU) - 1; |
| w = write(fd, EVENTS_SCRIPT_UBUNTU, len); |
| close(fd); |
| if (len != w) { |
| fprintf(stderr, "Error writing " |
| EVENTS_FILE_UBUNTU ": %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| return 0; |
| } else if (!stat(EVENTS_DIR, &st)) { |
| fd = open(EVENTS_FILE, O_WRONLY|O_TRUNC|O_CREAT, 0644); |
| if (fd == -1) { |
| fprintf(stderr, "Unable to create " EVENTS_FILE |
| ": %s\n", strerror(errno)); |
| return -1; |
| } |
| len = sizeof(EVENTS_SCRIPT) - 1; |
| w = write(fd, EVENTS_SCRIPT, len); |
| close(fd); |
| if (len != w) { |
| fprintf(stderr, "Error writing " EVENTS_FILE ": %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| return 0; |
| } |
| /* Add a line to /etc/inittab */ |
| if ((fd = open(INITTAB_FILE, O_RDWR | O_APPEND)) == -1) { |
| fprintf(stderr, "Unable to open " INITTAB_FILE " %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| ret = 0; |
| found = 0; |
| while (1) { |
| len = read(fd, buf, sizeof(buf)); |
| if (len == 0) |
| break; |
| if (len < 0) { |
| fprintf(stderr, "Unable to read from " INITTAB_FILE |
| " %s\n", |
| strerror(errno)); |
| ret = -1; |
| break; |
| } |
| buf[len] = 0; |
| if (strstr(buf, "\n" INITTAB_VZID) != NULL) { |
| found = 1; |
| break; |
| } |
| } |
| if (!found) { |
| if (write(fd, INITTAB_ACTION, sizeof(INITTAB_ACTION) - 1) == -1) |
| { |
| fprintf(stderr, "Unable to write to " INITTAB_FILE |
| " %s\n", |
| strerror(errno)); |
| ret = -1; |
| } |
| } |
| close(fd); |
| return ret; |
| } |
| |
| static void alarm_handler(int sig) |
| { |
| alarm_flag = 1; |
| } |
| |
| int wait_on_fifo(void *data) |
| { |
| int fd, buf, ret; |
| struct sigaction act, actold; |
| |
| ret = 0; |
| alarm_flag = 0; |
| act.sa_flags = 0; |
| act.sa_handler = alarm_handler; |
| sigemptyset(&act.sa_mask); |
| sigaction(SIGALRM, &act, &actold); |
| |
| alarm(MAX_WAIT_TIMEOUT); |
| fd = open(VZFIFO_FILE, O_RDONLY); |
| if (fd == -1) { |
| fprintf(stderr, "Unable to open " VZFIFO_FILE " %s\n", |
| strerror(errno)); |
| ret = -1; |
| goto err; |
| } |
| if (read(fd, &buf, sizeof(buf)) == -1) |
| ret = -1; |
| err: |
| if (alarm_flag) |
| ret = VZ_EXEC_TIMEOUT; |
| alarm(0); |
| sigaction(SIGALRM, &actold, NULL); |
| unlink(VZFIFO_FILE); |
| return ret; |
| } |