Skip to content

Instantly share code, notes, and snippets.

@jmlvanre
Last active August 29, 2015 14:24
Show Gist options
  • Save jmlvanre/c245ae314ce2f66d6c92 to your computer and use it in GitHub Desktop.
Save jmlvanre/c245ae314ce2f66d6c92 to your computer and use it in GitHub Desktop.
Understanding Cgroup Migration on SystemD

#Make the following files:

Note: depending on your system you may need to modify the following variables SYSTEMD_LIB_DIR and GROUP_DIR

bad-cgroup-demo.service

[Unit]
Description=Systemd bad cgroup demo

[Service]
Type=forking
ExecStart=/tmp/systemd-demo-unit
StandardOutput=journal
Delegate=false

[Install]
WantedBy=multi-user.target
Alias=bad-cgroup-demo.service

good-cgroup-demo.service

[Unit]
Description=Systemd good cgroup demo

[Service]
Type=forking
ExecStart=/tmp/systemd-demo-unit
StandardOutput=journal
Delegate=true

[Install]
WantedBy=multi-user.target
Alias=good-cgroup-demo.service

systemd-demo-unit.cc

#include <fstream>
#include <unordered_set>

#include <sys/stat.h>

#include <unistd.h>

using std::string;
using std::ofstream;
using std::ifstream;
using std::unordered_set;

const std::string GROUP_DIR = "/sys/fs/cgroup/";
const std::string SUBSYSTEM = "memory";
const std::string PROC_NAME = "systemd-demo-unit";
const std::string PROC_PATH = GROUP_DIR + SUBSYSTEM + "/" + PROC_NAME;
const std::string TASKS_PATH = PROC_PATH + "/tasks";

void force_proc_dir()
{
  struct stat st;
  if (stat(PROC_PATH.c_str(), &st) == 0) {
    if(!(st.st_mode & S_IFDIR)) { exit(EXIT_FAILURE); }
    // If we get here the directory already exists.
  } else {
    if (mkdir(PROC_PATH.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { exit(EXIT_FAILURE); }
  }
}

void add_task(pid_t pid)
{
  ofstream out(TASKS_PATH.c_str());
  if (!out.is_open()) { exit(EXIT_FAILURE); }
  out << pid;
  if (out.fail()) { exit(EXIT_FAILURE); }
  out.close();
}

unordered_set<pid_t> get_tasks()
{
  unordered_set<pid_t> result;
  ifstream in(TASKS_PATH.c_str());

  while (in.is_open() && !in.bad() && !in.eof() && !in.fail()) {
    pid_t pid;
    in >> pid;
    result.emplace(pid);
  }

  in.close();
  return result;
}

int main()
{
  pid_t pid = fork();

  if (pid < 0) {
    // Bail on failed fork.
    exit(EXIT_FAILURE);
  }

  if (pid == 0) {
    // Child.
    sleep(3);

    auto tasks = get_tasks();
    for (const pid_t pid : tasks) {
      printf("Child task view = [%d]\n", pid);
    }

    printf("Reloading daemon\n");
    system("systemctl daemon-reload");
    printf("Restarting syslog\n");
    system("systemctl restart rsyslog.service");

    sleep(3);

    tasks = get_tasks();
    for (const pid_t pid : tasks) {
      printf("Child task view = [%d]\n", pid);
    }

    return 0;
  } else {
    // Parent.

    // Make sure the cgroup dir exists.
    force_proc_dir();

    sleep(1);

    // Write the child pid to the cgroup tasks.
    printf("Adding child task [%d] to tasks\n", pid);
    add_task(pid);

    auto tasks = get_tasks();
    for (const pid_t pid : tasks) {
      printf("Parent task view = [%d]\n", pid);
    }

    printf("Parent exiting\n");
  }

  return 0;
}

cgroup-demo.sh

#!/usr/bin/env bash

SYSTEMD_LIB_DIR=/lib/systemd/system/

if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi

cp good-cgroup-demo.service $SYSTEMD_LIB_DIR
cp bad-cgroup-demo.service $SYSTEMD_LIB_DIR

systemctl daemon-reload

echo "Running Bad Cgroup Demo:"

systemctl start bad-cgroup-demo.service
sleep 10
journalctl -u bad-cgroup-demo.service -n9

echo "Running Good Cgroup Demo:";

systemctl start good-cgroup-demo.service
sleep 10
journalctl -u good-cgroup-demo.service -n9

echo "Cleaning up..."

rm "$SYSTEMD_LIB_DIR""good-cgroup-demo.service"
rm "$SYSTEMD_LIB_DIR""bad-cgroup-demo.service"

echo "Finished"

#Run the following commands:

// Allow the script to be executed.
chmod +x cgroup-demo.sh
// Compile the demo binary.
clang++-3.6 -std=c++11 systemd-demo-unit.cc -o systemd-demo-unit
// Copy the binary to /tmp
cp systemd-demo-unit /tmp/
// Run the script as ROOT
./cgroup-demo.sh

###You should see output similar to this:

Running Bad Cgroup Demo:
-- Logs begin at ***, end at ***. --
DDD 12:51:53 XXX systemd[1]: Starting Systemd bad cgroup demo...
DDD 12:51:54 XXX systemd-demo-unit[13824]: Adding child task [13825] to tasks
DDD 12:51:54 XXX systemd-demo-unit[13824]: Parent task view = [13825]
DDD 12:51:54 XXX systemd-demo-unit[13824]: Parent exiting
DDD 12:51:54 XXX systemd[1]: Started Systemd bad cgroup demo.
DDD 12:51:59 XXX systemd-demo-unit[13824]: Child task view = [13825]
DDD 12:51:59 XXX systemd-demo-unit[13824]: Reloading daemon
DDD 12:51:59 XXX systemd-demo-unit[13824]: Restarting syslog
DDD 12:51:59 XXX systemd-demo-unit[13824]: Child task view = [0]
Running Good Cgroup Demo:
-- Logs begin at *** , end at ***. --
DDD 12:52:04 XXX systemd[1]: Starting Systemd good cgroup demo...
DDD 12:52:05 XXX systemd-demo-unit[13858]: Adding child task [13859] to tasks
DDD 12:52:05 XXX systemd-demo-unit[13858]: Parent task view = [13859]
DDD 12:52:05 XXX systemd-demo-unit[13858]: Parent exiting
DDD 12:52:05 XXX systemd[1]: Started Systemd good cgroup demo.
DDD 12:52:10 XXX systemd-demo-unit[13858]: Child task view = [13859]
DDD 12:52:10 XXX systemd-demo-unit[13858]: Reloading daemon
DDD 12:52:10 XXX systemd-demo-unit[13858]: Restarting syslog
DDD 12:52:10 XXX systemd-demo-unit[13858]: Child task view = [13859]
Cleaning up...
Finished

Notice that when the Delegate is set to false, the child task gets removed from the cgroup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment