Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  cgroup_util.c   Sprache: C

 
/* SPDX-License-Identifier: GPL-2.0 */

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "cgroup_util.h"
#include "../../clone3/clone3_selftests.h"

bool cg_test_v1_named;

/* Returns read len on success, or -errno on failure. */
ssize_t read_text(const char *path, char *buf, size_t max_len)
{
 ssize_t len;
 int fd;

 fd = open(path, O_RDONLY);
 if (fd < 0)
  return -errno;

 len = read(fd, buf, max_len - 1);

 if (len >= 0)
  buf[len] = 0;

 close(fd);
 return len < 0 ? -errno : len;
}

/* Returns written len on success, or -errno on failure. */
ssize_t write_text(const char *path, char *buf, ssize_t len)
{
 int fd;

 fd = open(path, O_WRONLY | O_APPEND);
 if (fd < 0)
  return -errno;

 len = write(fd, buf, len);
 close(fd);
 return len < 0 ? -errno : len;
}

char *cg_name(const char *root, const char *name)
{
 size_t len = strlen(root) + strlen(name) + 2;
 char *ret = malloc(len);

 snprintf(ret, len, "%s/%s", root, name);

 return ret;
}

char *cg_name_indexed(const char *root, const char *name, int index)
{
 size_t len = strlen(root) + strlen(name) + 10;
 char *ret = malloc(len);

 snprintf(ret, len, "%s/%s_%d", root, name, index);

 return ret;
}

char *cg_control(const char *cgroup, const char *control)
{
 size_t len = strlen(cgroup) + strlen(control) + 2;
 char *ret = malloc(len);

 snprintf(ret, len, "%s/%s", cgroup, control);

 return ret;
}

/* Returns 0 on success, or -errno on failure. */
int cg_read(const char *cgroup, const char *control, char *buf, size_t len)
{
 char path[PATH_MAX];
 ssize_t ret;

 snprintf(path, sizeof(path), "%s/%s", cgroup, control);

 ret = read_text(path, buf, len);
 return ret >= 0 ? 0 : ret;
}

int cg_read_strcmp(const char *cgroup, const char *control,
     const char *expected)
{
 size_t size;
 char *buf;
 int ret;

 /* Handle the case of comparing against empty string */
 if (!expected)
  return -1;
 else
  size = strlen(expected) + 1;

 buf = malloc(size);
 if (!buf)
  return -1;

 if (cg_read(cgroup, control, buf, size)) {
  free(buf);
  return -1;
 }

 ret = strcmp(expected, buf);
 free(buf);
 return ret;
}

int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
{
 char buf[PAGE_SIZE];

 if (cg_read(cgroup, control, buf, sizeof(buf)))
  return -1;

 return strstr(buf, needle) ? 0 : -1;
}

long cg_read_long(const char *cgroup, const char *control)
{
 char buf[128];

 if (cg_read(cgroup, control, buf, sizeof(buf)))
  return -1;

 return atol(buf);
}

long cg_read_long_fd(int fd)
{
 char buf[128];

 if (pread(fd, buf, sizeof(buf), 0) <= 0)
  return -1;

 return atol(buf);
}

long cg_read_key_long(const char *cgroup, const char *control, const char *key)
{
 char buf[PAGE_SIZE];
 char *ptr;

 if (cg_read(cgroup, control, buf, sizeof(buf)))
  return -1;

 ptr = strstr(buf, key);
 if (!ptr)
  return -1;

 return atol(ptr + strlen(key));
}

long cg_read_lc(const char *cgroup, const char *control)
{
 char buf[PAGE_SIZE];
 const char delim[] = "\n";
 char *line;
 long cnt = 0;

 if (cg_read(cgroup, control, buf, sizeof(buf)))
  return -1;

 for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
  cnt++;

 return cnt;
}

/* Returns 0 on success, or -errno on failure. */
int cg_write(const char *cgroup, const char *control, char *buf)
{
 char path[PATH_MAX];
 ssize_t len = strlen(buf), ret;

 snprintf(path, sizeof(path), "%s/%s", cgroup, control);
 ret = write_text(path, buf, len);
 return ret == len ? 0 : ret;
}

/*
 * Returns fd on success, or -1 on failure.
 * (fd should be closed with close() as usual)
 */

int cg_open(const char *cgroup, const char *control, int flags)
{
 char path[PATH_MAX];

 snprintf(path, sizeof(path), "%s/%s", cgroup, control);
 return open(path, flags);
}

int cg_write_numeric(const char *cgroup, const char *control, long value)
{
 char buf[64];
 int ret;

 ret = sprintf(buf, "%lu", value);
 if (ret < 0)
  return ret;

 return cg_write(cgroup, control, buf);
}

static int cg_find_root(char *root, size_t len, const char *controller,
   bool *nsdelegate)
{
 char buf[10 * PAGE_SIZE];
 char *fs, *mount, *type, *options;
 const char delim[] = "\n\t ";

 if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0)
  return -1;

 /*
 * Example:
 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0
 */

 for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) {
  mount = strtok(NULL, delim);
  type = strtok(NULL, delim);
  options = strtok(NULL, delim);
  strtok(NULL, delim);
  strtok(NULL, delim);
  if (strcmp(type, "cgroup") == 0) {
   if (!controller || !strstr(options, controller))
    continue;
  } else if (strcmp(type, "cgroup2") == 0) {
   if (controller &&
     cg_read_strstr(mount, "cgroup.controllers", controller))
    continue;
  } else {
   continue;
  }
  strncpy(root, mount, len);

  if (nsdelegate)
   *nsdelegate = !!strstr(options, "nsdelegate");
  return 0;

 }

 return -1;
}

int cg_find_controller_root(char *root, size_t len, const char *controller)
{
 return cg_find_root(root, len, controller, NULL);
}

int cg_find_unified_root(char *root, size_t len, bool *nsdelegate)
{
 return cg_find_root(root, len, NULL, nsdelegate);
}

int cg_create(const char *cgroup)
{
 return mkdir(cgroup, 0755);
}

int cg_wait_for_proc_count(const char *cgroup, int count)
{
 char buf[10 * PAGE_SIZE] = {0};
 int attempts;
 char *ptr;

 for (attempts = 10; attempts >= 0; attempts--) {
  int nr = 0;

  if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
   break;

  for (ptr = buf; *ptr; ptr++)
   if (*ptr == '\n')
    nr++;

  if (nr >= count)
   return 0;

  usleep(100000);
 }

 return -1;
}

int cg_killall(const char *cgroup)
{
 char buf[PAGE_SIZE];
 char *ptr = buf;

 /* If cgroup.kill exists use it. */
 if (!cg_write(cgroup, "cgroup.kill""1"))
  return 0;

 if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
  return -1;

 while (ptr < buf + sizeof(buf)) {
  int pid = strtol(ptr, &ptr, 10);

  if (pid == 0)
   break;
  if (*ptr)
   ptr++;
  else
   break;
  if (kill(pid, SIGKILL))
   return -1;
 }

 return 0;
}

int cg_destroy(const char *cgroup)
{
 int ret;

 if (!cgroup)
  return 0;
retry:
 ret = rmdir(cgroup);
 if (ret && errno == EBUSY) {
  cg_killall(cgroup);
  usleep(100);
  goto retry;
 }

 if (ret && errno == ENOENT)
  ret = 0;

 return ret;
}

int cg_enter(const char *cgroup, int pid)
{
 char pidbuf[64];

 snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
 return cg_write(cgroup, "cgroup.procs", pidbuf);
}

int cg_enter_current(const char *cgroup)
{
 return cg_write(cgroup, "cgroup.procs""0");
}

int cg_enter_current_thread(const char *cgroup)
{
 return cg_write(cgroup, CG_THREADS_FILE, "0");
}

int cg_run(const char *cgroup,
    int (*fn)(const char *cgroup, void *arg),
    void *arg)
{
 int pid, retcode;

 pid = fork();
 if (pid < 0) {
  return pid;
 } else if (pid == 0) {
  char buf[64];

  snprintf(buf, sizeof(buf), "%d", getpid());
  if (cg_write(cgroup, "cgroup.procs", buf))
   exit(EXIT_FAILURE);
  exit(fn(cgroup, arg));
 } else {
  waitpid(pid, &retcode, 0);
  if (WIFEXITED(retcode))
   return WEXITSTATUS(retcode);
  else
   return -1;
 }
}

pid_t clone_into_cgroup(int cgroup_fd)
{
#ifdef CLONE_ARGS_SIZE_VER2
 pid_t pid;

 struct __clone_args args = {
  .flags = CLONE_INTO_CGROUP,
  .exit_signal = SIGCHLD,
  .cgroup = cgroup_fd,
 };

 pid = sys_clone3(&args, sizeof(struct __clone_args));
 /*
 * Verify that this is a genuine test failure:
 * ENOSYS -> clone3() not available
 * E2BIG  -> CLONE_INTO_CGROUP not available
 */

 if (pid < 0 && (errno == ENOSYS || errno == E2BIG))
  goto pretend_enosys;

 return pid;

pretend_enosys:
#endif
 errno = ENOSYS;
 return -ENOSYS;
}

int clone_reap(pid_t pid, int options)
{
 int ret;
 siginfo_t info = {
  .si_signo = 0,
 };

again:
 ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD);
 if (ret < 0) {
  if (errno == EINTR)
   goto again;
  return -1;
 }

 if (options & WEXITED) {
  if (WIFEXITED(info.si_status))
   return WEXITSTATUS(info.si_status);
 }

 if (options & WSTOPPED) {
  if (WIFSTOPPED(info.si_status))
   return WSTOPSIG(info.si_status);
 }

 if (options & WCONTINUED) {
  if (WIFCONTINUED(info.si_status))
   return 0;
 }

 return -1;
}

int dirfd_open_opath(const char *dir)
{
 return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH);
}

#define close_prot_errno(fd)                                                   \
 if (fd >= 0) {                                                         \
  int _e_ = errno;                                               \
  close(fd);                                                     \
  errno = _e_;                                                   \
 }

static int clone_into_cgroup_run_nowait(const char *cgroup,
     int (*fn)(const char *cgroup, void *arg),
     void *arg)
{
 int cgroup_fd;
 pid_t pid;

 cgroup_fd =  dirfd_open_opath(cgroup);
 if (cgroup_fd < 0)
  return -1;

 pid = clone_into_cgroup(cgroup_fd);
 close_prot_errno(cgroup_fd);
 if (pid == 0)
  exit(fn(cgroup, arg));

 return pid;
}

int cg_run_nowait(const char *cgroup,
    int (*fn)(const char *cgroup, void *arg),
    void *arg)
{
 int pid;

 pid = clone_into_cgroup_run_nowait(cgroup, fn, arg);
 if (pid > 0)
  return pid;

 /* Genuine test failure. */
 if (pid < 0 && errno != ENOSYS)
  return -1;

 pid = fork();
 if (pid == 0) {
  char buf[64];

  snprintf(buf, sizeof(buf), "%d", getpid());
  if (cg_write(cgroup, "cgroup.procs", buf))
   exit(EXIT_FAILURE);
  exit(fn(cgroup, arg));
 }

 return pid;
}

int proc_mount_contains(const char *option)
{
 char buf[4 * PAGE_SIZE];
 ssize_t read;

 read = read_text("/proc/mounts", buf, sizeof(buf));
 if (read < 0)
  return read;

 return strstr(buf, option) != NULL;
}

int cgroup_feature(const char *feature)
{
 char buf[PAGE_SIZE];
 ssize_t read;

 read = read_text("/sys/kernel/cgroup/features", buf, sizeof(buf));
 if (read < 0)
  return read;

 return strstr(buf, feature) != NULL;
}

ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size)
{
 char path[PATH_MAX];
 ssize_t ret;

 if (!pid)
  snprintf(path, sizeof(path), "/proc/%s/%s",
    thread ? "thread-self" : "self", item);
 else
  snprintf(path, sizeof(path), "/proc/%d/%s", pid, item);

 ret = read_text(path, buf, size);
 return ret < 0 ? -1 : ret;
}

int proc_read_strstr(int pid, bool thread, const char *item, const char *needle)
{
 char buf[PAGE_SIZE];

 if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0)
  return -1;

 return strstr(buf, needle) ? 0 : -1;
}

int clone_into_cgroup_run_wait(const char *cgroup)
{
 int cgroup_fd;
 pid_t pid;

 cgroup_fd =  dirfd_open_opath(cgroup);
 if (cgroup_fd < 0)
  return -1;

 pid = clone_into_cgroup(cgroup_fd);
 close_prot_errno(cgroup_fd);
 if (pid < 0)
  return -1;

 if (pid == 0)
  exit(EXIT_SUCCESS);

 /*
 * We don't care whether this fails. We only care whether the initial
 * clone succeeded.
 */

 (void)clone_reap(pid, WEXITED);
 return 0;
}

static int __prepare_for_wait(const char *cgroup, const char *filename)
{
 int fd, ret = -1;

 fd = inotify_init1(0);
 if (fd == -1)
  return fd;

 ret = inotify_add_watch(fd, cg_control(cgroup, filename), IN_MODIFY);
 if (ret == -1) {
  close(fd);
  fd = -1;
 }

 return fd;
}

int cg_prepare_for_wait(const char *cgroup)
{
 return __prepare_for_wait(cgroup, "cgroup.events");
}

int memcg_prepare_for_wait(const char *cgroup)
{
 return __prepare_for_wait(cgroup, "memory.events");
}

int cg_wait_for(int fd)
{
 int ret = -1;
 struct pollfd fds = {
  .fd = fd,
  .events = POLLIN,
 };

 while (true) {
  ret = poll(&fds, 1, 10000);

  if (ret == -1) {
   if (errno == EINTR)
    continue;

   break;
  }

  if (ret > 0 && fds.revents & POLLIN) {
   ret = 0;
   break;
  }
 }

 return ret;
}

Messung V0.5
C=97 H=91 G=93

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge