/* * cpuplugd - Linux for System z Hotplug Daemon * * Daemon functions * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "cpuplugd.h" const char *name = NAME; static const char *pid_file = PIDFILE; const char *const usage = "Usage: %s [OPTIONS]\n" "\n" "Daemon to dynamically hotplug cpus and memory based on a set of rules\n" "Use OPTIONS described below.\n" "\n" "\t-c, --config CONFIGFILE Path to the configuration file\n" "\t-f, --foreground Run in foreground, do not detach\n" "\t-h, --help Print this help, then exit\n" "\t-v, --version Print version information, then exit\n" "\t-V, --verbose Provide more verbose output\n"; /* * Print command usage */ void print_usage(int is_error, char program_name[]) { fprintf(is_error ? stderr : stdout, usage, program_name); exit(is_error ? 1 : 0); } /* * Print command version */ void print_version() { printf("%s: Linux on System z CPU hotplug daemon version %s\n", name, RELEASE_STRING); printf("Copyright IBM Corp. 2007, 2017\n"); exit(0); } /* * Store daemon's pid so it can be stopped */ static int store_pid(void) { FILE *filp; filp = fopen(pid_file, "w"); if (!filp) { cpuplugd_error("cannot open pid file %s: %s\n", pid_file, strerror(errno)); return -1; } fprintf(filp, "%d\n", getpid()); fclose(filp); return 0; } /* * Run daemon in background and write pid file */ int daemonize(void) { int fd, pipe_fds[2], startup_rc = 1; pid_t pid; if (pipe(pipe_fds) == -1) { cpuplugd_error("cannot create pipe\n"); return -1; } pid = fork(); if (pid < 0) goto close_pipe; if (pid != 0) { /* Wait for startup return code from daemon */ if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1) cpuplugd_error("cannot read from pipe\n"); /* On success daemon has written pid file at this point */ exit(startup_rc); } /* Create new session */ if (setsid() < 0) goto notify_parent; /* Redirect stdin/out/err to /dev/null */ fd = open("/dev/null", O_RDWR, 0); if (fd == -1) goto notify_parent; if (dup2(fd, STDIN_FILENO) < 0) goto notify_parent; if (dup2(fd, STDOUT_FILENO) < 0) goto notify_parent; if (dup2(fd, STDERR_FILENO) < 0) goto notify_parent; /* Create pid file */ if (store_pid() < 0) goto notify_parent; startup_rc = 0; notify_parent: /* Inform waiting parent about startup return code */ if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) { cpuplugd_error("cannot write to pipe\n"); startup_rc = 1; } close_pipe: close(pipe_fds[0]); close(pipe_fds[1]); return startup_rc ? -1 : 0; } /* * Check that we don't try to start this daemon twice */ void check_if_started_twice() { FILE *filp; int pid, rc; filp = fopen(pid_file, "r"); if (filp) { rc = fscanf(filp, "%d", &pid); if (rc != 1) { cpuplugd_error("Reading pid file failed. Aborting!\n"); exit(1); } cpuplugd_error("pid file %s still exists.\nThis might indicate " "that an instance of this daemon is already " "running.\n", pid_file); exit(1); } } /* * Clean up method */ void clean_up() { cpuplugd_info("terminated\n"); remove(pid_file); remove(LOCKFILE); reactivate_cpus(); if (memory) cleanup_cmm(); exit(1); } /* * End the deamon */ void kill_daemon(int UNUSED(a)) { cpuplugd_info("shutting down\n"); remove(pid_file); remove(LOCKFILE); reactivate_cpus(); if (memory) cleanup_cmm(); exit(0); } /* * Reload the daemon (for lsb compliance) */ void reload_handler(int UNUSED(a)) { reload_pending = 1; } void reload_daemon() { unsigned int temp_history; long temp_mem; int temp_cpu; cpuplugd_info("cpuplugd restarted\n"); /* * Before we parse the configuration file again we have to save * the original values prior to startup. If we don't do this cpuplugd * will no longer know how many cpus the system had before the daemon * was started and therefor can't restore theres in case it is stopped */ temp_cpu = num_cpu_start; temp_mem = cmm_pagesize_start; temp_history = history_max; /* clear varinfo before re-reading variables from config file */ memset(varinfo, 0, varinfo_size); history_max = 1; parse_configfile(configfile); if (history_max > MAX_HISTORY) cpuplugd_exit("History depth %i exceeded maximum (%i)\n", history_max, MAX_HISTORY); if (history_max != temp_history) { free(meminfo); free(vmstat); free(cpustat); free(timestamps); setup_history(); } check_config(); num_cpu_start = temp_cpu; cmm_pagesize_start = temp_mem; } /* * Set up for handling SIGTERM or SIGINT */ void handle_signals(void) { struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = kill_daemon; if (sigaction(SIGTERM, &act, NULL) < 0) { cpuplugd_error("sigaction( SIGTERM, ... ) failed - reason %s\n", strerror(errno)); exit(1); } if (sigaction(SIGINT, &act, NULL) < 0) { cpuplugd_error("sigaction( SIGINT, ... ) failed - reason %s\n", strerror(errno)); exit(1); } } /* * Signal handler for sighup. This is used to force the deamon to reload its * configuration file. * This feature is also required by a lsb compliant init script */ void handle_sighup(void) { struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = reload_handler; if (sigaction(SIGHUP, &act, NULL) < 0) { cpuplugd_error("sigaction( SIGHUP, ... ) failed - reason %s\n", strerror(errno)); exit(1); } } /* Check if we are running in an LPAR environment. * This functions return 1 if we run inside an lpar and 0 otherwise */ int check_lpar() { int rc; FILE *filp; size_t bytes_read; char buffer[2048]; char *contains_vm; rc = 0; filp = fopen("/proc/cpuinfo", "r"); if (!filp) cpuplugd_exit("cannot open /proc/cpuinfo: %s\n", strerror(errno)); bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp); if (bytes_read == 0) cpuplugd_exit("Reading /proc/cpuinfo failed: %s\n", strerror(errno)); /* NUL-terminate the text */ buffer[bytes_read] = '\0'; contains_vm = strstr(buffer, "version = FF"); if (contains_vm == NULL) { rc = 1; cpuplugd_debug("Detected System running in LPAR mode\n"); } else cpuplugd_debug("Detected System running in z/VM mode\n"); fclose(filp); return rc; }