mirror of
https://github.com/containers/crun.git
synced 2026-02-06 18:46:42 +01:00
951 lines
23 KiB
C
951 lines
23 KiB
C
/*
|
|
* crun - OCI runtime written in C
|
|
*
|
|
* Copyright (C) 2017, 2018, 2019 Giuseppe Scrivano <giuseppe@scrivano.org>
|
|
* crun 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.
|
|
*
|
|
* crun 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 crun. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#define _GNU_SOURCE
|
|
|
|
#include <config.h>
|
|
#include <linux/limits.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sched.h>
|
|
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
|
|
#ifdef HAVE_LINUX_IOPRIO_H
|
|
# include <linux/ioprio.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SECCOMP
|
|
# include <linux/seccomp.h>
|
|
# include <linux/filter.h>
|
|
# include <seccomp.h>
|
|
|
|
# ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
|
|
# define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
|
|
# endif
|
|
|
|
# ifndef SECCOMP_SET_MODE_FILTER
|
|
# define SECCOMP_SET_MODE_FILTER 1
|
|
# endif
|
|
|
|
# ifndef __NR_seccomp
|
|
# define __NR_seccomp 0xffff // seccomp syscall number unknown for this architecture
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_ERROR_H
|
|
# include <error.h>
|
|
#else
|
|
# define error(status, errno, fmt, ...) \
|
|
do \
|
|
{ \
|
|
if (errno == 0) \
|
|
fprintf (stderr, "crun: " fmt "\n", ##__VA_ARGS__); \
|
|
else \
|
|
{ \
|
|
fprintf (stderr, "crun: " fmt, ##__VA_ARGS__); \
|
|
fprintf (stderr, ": %s\n", strerror (errno)); \
|
|
} \
|
|
if (status) \
|
|
exit (status); \
|
|
} while (0)
|
|
#endif
|
|
|
|
#ifdef HAVE_LINUX_IOPRIO_H
|
|
static int
|
|
syscall_ioprio_get (int which, int who)
|
|
{
|
|
# ifdef __NR_ioprio_get
|
|
return syscall (__NR_ioprio_get, which, who);
|
|
# else
|
|
(void) which;
|
|
(void) who;
|
|
(void) ioprio;
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
struct mount_attr_s
|
|
{
|
|
uint64_t attr_set;
|
|
uint64_t attr_clr;
|
|
uint64_t propagation;
|
|
uint64_t userns_fd;
|
|
};
|
|
|
|
#ifndef MOUNT_ATTR_IDMAP
|
|
# define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
|
|
#endif
|
|
|
|
#ifndef OPEN_TREE_CLONE
|
|
# define OPEN_TREE_CLONE 1
|
|
#endif
|
|
|
|
#ifndef OPEN_TREE_CLOEXEC
|
|
# define OPEN_TREE_CLOEXEC O_CLOEXEC
|
|
#endif
|
|
|
|
static int
|
|
syscall_clone (unsigned long flags, void *child_stack)
|
|
{
|
|
#if defined __s390__ || defined __CRIS__
|
|
return (int) syscall (__NR_clone, child_stack, flags);
|
|
#else
|
|
return (int) syscall (__NR_clone, flags, child_stack);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
syscall_open_tree (int dfd, const char *pathname, unsigned int flags)
|
|
{
|
|
#if defined __NR_open_tree
|
|
return (int) syscall (__NR_open_tree, dfd, pathname, flags);
|
|
#else
|
|
(void) dfd;
|
|
(void) pathname;
|
|
(void) flags;
|
|
errno = ENOSYS;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
syscall_mount_setattr (int dfd, const char *path, unsigned int flags,
|
|
struct mount_attr_s *attr)
|
|
{
|
|
#ifdef __NR_mount_setattr
|
|
return (int) syscall (__NR_mount_setattr, dfd, path, flags, attr, sizeof (*attr));
|
|
#else
|
|
(void) dfd;
|
|
(void) path;
|
|
(void) flags;
|
|
(void) attr;
|
|
errno = ENOSYS;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
write_to (const char *path, const char *str)
|
|
{
|
|
int fd = open (path, O_WRONLY);
|
|
if (fd < 0)
|
|
error (EXIT_FAILURE, errno, "open `%s`", path);
|
|
|
|
if (write (fd, str, strlen (str)) < 0)
|
|
error (EXIT_FAILURE, errno, "write to `%s`", path);
|
|
if (close (fd) < 0)
|
|
error (EXIT_FAILURE, errno, "close file `%s`", path);
|
|
}
|
|
|
|
/*
|
|
Check that the file system at the specified path supports
|
|
idmapped mounts.
|
|
*/
|
|
__attribute__ ((noreturn)) static void
|
|
check_idmapped_mounts (const char *path)
|
|
{
|
|
struct mount_attr_s attr = {
|
|
0,
|
|
};
|
|
char proc_path[64];
|
|
int open_tree_fd;
|
|
pid_t pid;
|
|
int fd;
|
|
|
|
pid = syscall_clone (CLONE_NEWUSER | SIGCHLD, NULL);
|
|
if (pid < 0)
|
|
error (EXIT_FAILURE, errno, "clone");
|
|
if (pid == 0)
|
|
{
|
|
prctl (PR_SET_PDEATHSIG, SIGKILL);
|
|
while (1)
|
|
pause ();
|
|
_exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
snprintf (proc_path, sizeof (proc_path), "/proc/%d/uid_map", pid);
|
|
write_to (proc_path, "0 0 1");
|
|
snprintf (proc_path, sizeof (proc_path), "/proc/%d/gid_map", pid);
|
|
write_to (proc_path, "0 0 1");
|
|
|
|
snprintf (proc_path, sizeof (proc_path), "/proc/%d/ns/user", pid);
|
|
fd = open (proc_path, O_RDONLY);
|
|
if (fd < 0)
|
|
error (EXIT_FAILURE, errno, "open `%s`", proc_path);
|
|
|
|
open_tree_fd = syscall_open_tree (-1, path,
|
|
AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
|
|
if (open_tree_fd < 0)
|
|
error (EXIT_FAILURE, errno, "open `%s`", path);
|
|
|
|
attr.attr_set = MOUNT_ATTR_IDMAP;
|
|
attr.userns_fd = fd;
|
|
|
|
if (syscall_mount_setattr (open_tree_fd, "", AT_EMPTY_PATH, &attr) < 0)
|
|
error (EXIT_FAILURE, errno, "mount_setattr `%s`", path);
|
|
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
cat (char *file)
|
|
{
|
|
FILE *f = fopen (file, "rbe");
|
|
char buf[512];
|
|
if (f == NULL)
|
|
error (EXIT_FAILURE, errno, "fopen");
|
|
while (1)
|
|
{
|
|
size_t s = fread (buf, 1, sizeof (buf), f);
|
|
if (s == 0)
|
|
{
|
|
if (feof (f))
|
|
{
|
|
fclose (f);
|
|
return 0;
|
|
}
|
|
fclose (f);
|
|
error (EXIT_FAILURE, errno, "fread");
|
|
}
|
|
s = fwrite (buf, 1, s, stdout);
|
|
if (s == 0)
|
|
error (EXIT_FAILURE, errno, "fwrite");
|
|
}
|
|
}
|
|
|
|
static int
|
|
open_only (char *file, int flags)
|
|
{
|
|
int fd = open (file, flags);
|
|
if (fd >= 0)
|
|
{
|
|
close (fd);
|
|
exit (0);
|
|
}
|
|
error (EXIT_FAILURE, errno, "could not open %s", file);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
ls (char *path)
|
|
{
|
|
DIR *dir = opendir (path);
|
|
if (dir == NULL)
|
|
error (EXIT_FAILURE, errno, "opendir");
|
|
|
|
for (;;)
|
|
{
|
|
struct dirent *de;
|
|
errno = 0;
|
|
de = readdir (dir);
|
|
if (de == NULL && errno)
|
|
error (EXIT_FAILURE, errno, "readdir");
|
|
if (de == NULL)
|
|
{
|
|
closedir (dir);
|
|
return 0;
|
|
}
|
|
printf ("%s\n", de->d_name);
|
|
}
|
|
closedir (dir);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sd_notify ()
|
|
{
|
|
int ret;
|
|
int notify_socket_fd;
|
|
char *notify_socket_name;
|
|
struct sockaddr_un notify_socket_unix_name;
|
|
const char *ready_data = "READY=1";
|
|
const int ready_data_len = 7;
|
|
|
|
notify_socket_name = getenv ("NOTIFY_SOCKET");
|
|
|
|
if (notify_socket_name == NULL)
|
|
error (EXIT_FAILURE, 0, "NOTIFY_SOCKET not found in environment");
|
|
|
|
notify_socket_fd = socket (AF_UNIX, SOCK_DGRAM, 0);
|
|
if (notify_socket_fd < 0)
|
|
error (EXIT_FAILURE, errno, "socket");
|
|
|
|
notify_socket_unix_name.sun_family = AF_UNIX;
|
|
strncpy (notify_socket_unix_name.sun_path, notify_socket_name,
|
|
sizeof (notify_socket_unix_name.sun_path));
|
|
|
|
ret = sendto (notify_socket_fd, ready_data, ready_data_len, 0,
|
|
(struct sockaddr *) ¬ify_socket_unix_name, sizeof (notify_socket_unix_name));
|
|
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, 0, "sendto");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
syscall_seccomp (unsigned int operation, unsigned int flags, void *args)
|
|
{
|
|
return (int) syscall (__NR_seccomp, operation, flags, args);
|
|
}
|
|
|
|
static void
|
|
do_pause ()
|
|
{
|
|
unsigned int remaining = 120;
|
|
|
|
close (1);
|
|
close (2);
|
|
|
|
while (remaining)
|
|
remaining = sleep (remaining);
|
|
|
|
exit (0);
|
|
}
|
|
|
|
static int
|
|
memhog (int megabytes)
|
|
{
|
|
char *buf;
|
|
int pos = 0;
|
|
|
|
if (megabytes < 1)
|
|
error (EXIT_FAILURE, 0, "memhog argument needs to be at least 1");
|
|
|
|
buf = malloc (megabytes * 1024 * 1024);
|
|
if (buf == NULL)
|
|
error (EXIT_FAILURE, 0, "malloc");
|
|
|
|
close (1);
|
|
close (2);
|
|
|
|
while (1)
|
|
{
|
|
/* write each page once */
|
|
buf[pos] = 'c';
|
|
pos += sysconf (_SC_PAGESIZE);
|
|
if (pos > megabytes * 1024 * 1024)
|
|
break;
|
|
}
|
|
|
|
pos = 0;
|
|
|
|
while (1)
|
|
{
|
|
/* change one page each 0.1 seconds */
|
|
nanosleep ((const struct timespec[]) { { 0, 100000000L } }, NULL);
|
|
buf[pos] = 'c';
|
|
pos += sysconf (_SC_PAGESIZE);
|
|
if (pos > megabytes * 1024 * 1024)
|
|
pos = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define BUFFER_SIZE 8192
|
|
|
|
static void
|
|
dump_net_interface (const char *ifname)
|
|
{
|
|
struct sockaddr_nl sa;
|
|
struct nlmsghdr *nlh;
|
|
struct ifaddrmsg *ifa;
|
|
char *buffer;
|
|
int ifindex;
|
|
int sock;
|
|
|
|
ifindex = if_nametoindex (ifname);
|
|
if (ifindex == 0)
|
|
error (EXIT_FAILURE, errno, "if_nametoindex `%s`", ifname);
|
|
|
|
sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
if (sock < 0)
|
|
error (EXIT_FAILURE, errno, "socket");
|
|
|
|
buffer = malloc (BUFFER_SIZE);
|
|
if (buffer == NULL)
|
|
error (EXIT_FAILURE, errno, "malloc");
|
|
|
|
sa.nl_family = AF_NETLINK;
|
|
|
|
if (bind (sock, (struct sockaddr *) &sa, sizeof (sa)) < 0)
|
|
error (EXIT_FAILURE, errno, "bind");
|
|
|
|
nlh = (struct nlmsghdr *) buffer;
|
|
|
|
memset (buffer, 0, BUFFER_SIZE);
|
|
nlh->nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
|
|
nlh->nlmsg_type = RTM_GETADDR;
|
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
|
nlh->nlmsg_seq = 1;
|
|
|
|
ifa = NLMSG_DATA (nlh);
|
|
ifa->ifa_family = AF_INET;
|
|
|
|
if (send (sock, nlh, nlh->nlmsg_len, 0) < 0)
|
|
error (EXIT_FAILURE, errno, "send");
|
|
|
|
while (1)
|
|
{
|
|
ssize_t len = recv (sock, buffer, BUFFER_SIZE, 0);
|
|
if (len < 0)
|
|
error (EXIT_FAILURE, errno, "recv");
|
|
|
|
for (nlh = (struct nlmsghdr *) buffer; NLMSG_OK (nlh, len); nlh = NLMSG_NEXT (nlh, len))
|
|
{
|
|
struct rtattr *rta[IFA_MAX + 1] = {};
|
|
struct rtattr *rta_it;
|
|
int rta_len;
|
|
|
|
if (nlh->nlmsg_type == NLMSG_DONE)
|
|
goto done;
|
|
if (nlh->nlmsg_type == NLMSG_ERROR)
|
|
error (EXIT_FAILURE, 0, "netlink error");
|
|
|
|
ifa = NLMSG_DATA (nlh);
|
|
if (ifa->ifa_index != ifindex)
|
|
continue;
|
|
|
|
rta_it = IFA_RTA (ifa);
|
|
rta_len = IFA_PAYLOAD (nlh);
|
|
|
|
while (RTA_OK (rta_it, rta_len))
|
|
{
|
|
if (rta_it->rta_type <= IFA_MAX)
|
|
rta[rta_it->rta_type] = rta_it;
|
|
rta_it = RTA_NEXT (rta_it, rta_len);
|
|
}
|
|
|
|
if (rta[IFA_ADDRESS])
|
|
{
|
|
char addr[INET_ADDRSTRLEN];
|
|
inet_ntop (AF_INET, RTA_DATA (rta[IFA_LOCAL]), addr, sizeof (addr));
|
|
printf ("address: %s/%d\n", addr, ifa->ifa_prefixlen);
|
|
}
|
|
if (rta[IFA_BROADCAST])
|
|
{
|
|
char bcast[INET_ADDRSTRLEN];
|
|
inet_ntop (AF_INET, RTA_DATA (rta[IFA_BROADCAST]), bcast, sizeof (bcast));
|
|
printf ("broadcast: %s\n", bcast);
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
close (sock);
|
|
free (buffer);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
if (argc < 2)
|
|
error (EXIT_FAILURE, 0, "specify at least one command");
|
|
|
|
if (strcmp (argv[1], "true") == 0)
|
|
{
|
|
exit (0);
|
|
}
|
|
|
|
if (strcmp (argv[1], "exit") == 0)
|
|
{
|
|
int code = 0;
|
|
if (argc >= 3)
|
|
code = atoi (argv[2]);
|
|
exit (code);
|
|
}
|
|
|
|
if (strcmp (argv[1], "echo") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'echo' requires an argument");
|
|
fputs (argv[2], stdout);
|
|
exit (0);
|
|
}
|
|
|
|
if (strcmp (argv[1], "printenv") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'printenv' requires an argument");
|
|
fputs (getenv (argv[2]), stdout);
|
|
exit (0);
|
|
}
|
|
|
|
if (strcmp (argv[1], "groups") == 0)
|
|
{
|
|
gid_t groups[10];
|
|
int max_groups = sizeof (groups) / sizeof (groups[0]);
|
|
int n_groups, i;
|
|
n_groups = getgroups (max_groups, groups);
|
|
fputs ("GROUPS=[", stdout);
|
|
for (i = 0; i < n_groups; i++)
|
|
printf ("%s%d", i == 0 ? "" : " ", groups[i]);
|
|
fputs ("]\n", stdout);
|
|
exit (0);
|
|
}
|
|
|
|
if (strcmp (argv[1], "cat") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'cat' requires an argument");
|
|
return cat (argv[2]);
|
|
}
|
|
|
|
if (strcmp (argv[1], "readlink") == 0)
|
|
{
|
|
ssize_t ret;
|
|
char *buf = malloc (PATH_MAX);
|
|
if (buf == NULL)
|
|
error (EXIT_FAILURE, errno, "malloc");
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'readlink' requires an argument");
|
|
|
|
ret = readlink (argv[2], buf, PATH_MAX);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "readlink");
|
|
printf ("%.*s", (int) ret, buf);
|
|
free (buf);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "open") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'open' requires an argument");
|
|
return open_only (argv[2], O_RDONLY);
|
|
}
|
|
|
|
if (strcmp (argv[1], "openwronly") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'openwronly' requires an argument");
|
|
return open_only (argv[2], O_WRONLY);
|
|
}
|
|
|
|
if (strcmp (argv[1], "access") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'access' requires an argument");
|
|
|
|
if (access (argv[2], F_OK) < 0)
|
|
error (EXIT_FAILURE, errno, "could not access %s", argv[2]);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "type") == 0)
|
|
{
|
|
struct stat st;
|
|
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'type' requires two arguments");
|
|
if (stat (argv[2], &st) < 0)
|
|
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
|
|
|
|
switch (st.st_mode & S_IFMT)
|
|
{
|
|
case S_IFBLK:
|
|
printf ("block device\n");
|
|
break;
|
|
case S_IFCHR:
|
|
printf ("character device\n");
|
|
break;
|
|
|
|
case S_IFDIR:
|
|
printf ("directory\n");
|
|
break;
|
|
|
|
case S_IFIFO:
|
|
printf ("FIFO/pipe\n");
|
|
break;
|
|
|
|
case S_IFLNK:
|
|
printf ("symlink\n");
|
|
break;
|
|
|
|
case S_IFREG:
|
|
printf ("regular file\n");
|
|
break;
|
|
|
|
case S_IFSOCK:
|
|
printf ("socket\n");
|
|
break;
|
|
|
|
default:
|
|
printf ("unknown?\n");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "isfifo") == 0)
|
|
{
|
|
struct stat st;
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'isfifo' requires a path argument");
|
|
if (stat (argv[2], &st) < 0)
|
|
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
|
|
if (S_ISFIFO (st.st_mode))
|
|
exit (0);
|
|
else
|
|
exit (1);
|
|
}
|
|
|
|
if (strcmp (argv[1], "ischar") == 0)
|
|
{
|
|
struct stat st;
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'ischar' requires a path argument");
|
|
if (stat (argv[2], &st) < 0)
|
|
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
|
|
if (S_ISCHR (st.st_mode))
|
|
exit (0);
|
|
else
|
|
exit (1);
|
|
}
|
|
|
|
if (strcmp (argv[1], "isblock") == 0)
|
|
{
|
|
struct stat st;
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'isblock' requires a path argument");
|
|
if (stat (argv[2], &st) < 0)
|
|
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
|
|
if (S_ISBLK (st.st_mode))
|
|
exit (0);
|
|
else
|
|
exit (1);
|
|
}
|
|
|
|
if (strcmp (argv[1], "owner") == 0)
|
|
{
|
|
struct stat st;
|
|
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'owner' requires two arguments");
|
|
if (stat (argv[2], &st) < 0)
|
|
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
|
|
|
|
printf ("%d:%d", st.st_uid, st.st_gid);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "mode") == 0)
|
|
{
|
|
struct stat st;
|
|
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'mode' requires two arguments");
|
|
if (stat (argv[2], &st) < 0)
|
|
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
|
|
|
|
printf ("%o", st.st_mode & 07777);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "id") == 0)
|
|
{
|
|
int ret;
|
|
|
|
ret = printf ("%d:%d", getuid (), getgid ());
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "printf");
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "cwd") == 0)
|
|
{
|
|
int ret;
|
|
char *wd = getcwd (NULL, 0);
|
|
if (wd == NULL)
|
|
error (EXIT_FAILURE, 0, "OOM");
|
|
|
|
ret = printf ("%s\n", wd);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "printf");
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "gethostname") == 0)
|
|
{
|
|
char buffer[64] = {};
|
|
int ret;
|
|
|
|
ret = gethostname (buffer, sizeof (buffer) - 1);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "gethostname");
|
|
|
|
ret = printf ("%s\n", buffer);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "printf");
|
|
return 0;
|
|
}
|
|
if (strcmp (argv[1], "getdomainname") == 0)
|
|
{
|
|
char buffer[64] = {};
|
|
int ret;
|
|
|
|
ret = getdomainname (buffer, sizeof (buffer) - 1);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "getdomainname");
|
|
|
|
ret = printf ("%s\n", buffer);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "printf");
|
|
return 0;
|
|
}
|
|
if (strcmp (argv[1], "isatty") == 0)
|
|
{
|
|
int fd;
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'isatty' requires two arguments");
|
|
fd = atoi (argv[2]);
|
|
printf (isatty (fd) ? "true" : "false");
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "ip") == 0)
|
|
{
|
|
if (argc < 2)
|
|
error (EXIT_FAILURE, 0, "'ip' requires an argument");
|
|
dump_net_interface (argv[2]);
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
if (strcmp (argv[1], "write") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'write' requires two arguments");
|
|
write_to (argv[2], argv[3]);
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
if (strcmp (argv[1], "pause") == 0)
|
|
{
|
|
do_pause ();
|
|
}
|
|
if (strcmp (argv[1], "memhog") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'memhog' requires an argument");
|
|
return memhog (atoi (argv[2]));
|
|
}
|
|
if (strcmp (argv[1], "create-sub-cgroup-and-wait") == 0)
|
|
{
|
|
char path[PATH_MAX];
|
|
int ret;
|
|
int fd;
|
|
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'create-sub-cgroup-and-wait' requires an argument");
|
|
|
|
snprintf (path, sizeof (path), "/sys/fs/cgroup/%s", argv[2]);
|
|
ret = mkdir (path, 0700);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "mkdir");
|
|
|
|
snprintf (path, sizeof (path), "/sys/fs/cgroup/%s/cgroup.procs", argv[2]);
|
|
|
|
fd = open (path, O_WRONLY);
|
|
if (fd < 0)
|
|
error (EXIT_FAILURE, errno, "open `%s`", path);
|
|
ret = write (fd, "1", 1);
|
|
if (ret < 0)
|
|
error (EXIT_FAILURE, errno, "open `%s`", path);
|
|
close (fd);
|
|
|
|
do_pause ();
|
|
}
|
|
if (strcmp (argv[1], "forkbomb") == 0)
|
|
{
|
|
int i, n;
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'forkbomb' requires two arguments");
|
|
n = atoi (argv[2]);
|
|
if (n < 0)
|
|
return 0;
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pid_t pid = fork ();
|
|
if (pid < 0)
|
|
error (EXIT_FAILURE, errno, "fork");
|
|
if (pid == 0)
|
|
sleep (100);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "ioprio") == 0)
|
|
{
|
|
#ifdef HAVE_LINUX_IOPRIO_H
|
|
int ret = syscall_ioprio_get (IOPRIO_WHO_PROCESS, 0);
|
|
if (ret >= 0)
|
|
{
|
|
printf ("%d\n", ret);
|
|
return 0;
|
|
}
|
|
error (EXIT_FAILURE, errno, "`ioprio_get` failed");
|
|
#else
|
|
error (EXIT_FAILURE, 0, "`ioprio_get` not supported");
|
|
#endif
|
|
}
|
|
|
|
if (strcmp (argv[1], "ls") == 0)
|
|
{
|
|
/* Fork so that ls /proc/1/fd doesn't show more fd's. */
|
|
pid_t pid;
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "'ls' requires two arguments");
|
|
pid = fork ();
|
|
if (pid < 0)
|
|
error (EXIT_FAILURE, errno, "fork");
|
|
if (pid)
|
|
{
|
|
int ret, status;
|
|
do
|
|
ret = waitpid (pid, &status, 0);
|
|
while (ret < 0 && errno == EINTR);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (WIFEXITED (status))
|
|
return WEXITSTATUS (status);
|
|
if (WIFSIGNALED (status))
|
|
return 128 + WTERMSIG (status);
|
|
return EXIT_FAILURE;
|
|
}
|
|
return ls (argv[2]);
|
|
}
|
|
|
|
if (strcmp (argv[1], "systemd-notify") == 0)
|
|
return sd_notify ();
|
|
|
|
if (strcmp (argv[1], "getpgrp") == 0)
|
|
{
|
|
pid_t pid = getpgrp ();
|
|
printf ("%d\n", pid);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp (argv[1], "check-feature") == 0)
|
|
{
|
|
if (argc < 3)
|
|
error (EXIT_FAILURE, 0, "`check-feature` requires an argument");
|
|
|
|
if (strcmp (argv[2], "ioprio") == 0)
|
|
{
|
|
#ifdef HAVE_LINUX_IOPRIO_H
|
|
if (syscall_ioprio_get (IOPRIO_WHO_PROCESS, 0) >= 0)
|
|
return 0;
|
|
#endif
|
|
return 1;
|
|
}
|
|
if (strcmp (argv[2], "idmapped-mounts") == 0)
|
|
{
|
|
if (argc < 4)
|
|
error (EXIT_FAILURE, 0, "`idmapped-mounts` requires an argument");
|
|
|
|
check_idmapped_mounts (argv[3]);
|
|
}
|
|
if (strcmp (argv[2], "open_tree") == 0)
|
|
{
|
|
#if defined __NR_open_tree
|
|
int ret;
|
|
|
|
ret = syscall (__NR_open_tree);
|
|
return (ret >= 0 || errno != ENOSYS) ? 0 : 1;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
else if (strcmp (argv[2], "move_mount") == 0)
|
|
{
|
|
#if defined __NR_move_mount
|
|
int ret;
|
|
|
|
ret = syscall (__NR_move_mount);
|
|
return (ret >= 0 || errno != ENOSYS) ? 0 : 1;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
else if (strcmp (argv[2], "seccomp-listener") == 0)
|
|
{
|
|
#ifdef HAVE_SECCOMP
|
|
int ret;
|
|
int p = fork ();
|
|
if (p < 0)
|
|
return 1;
|
|
if (p)
|
|
{
|
|
int status;
|
|
do
|
|
ret = waitpid (p, &status, 0);
|
|
while (ret < 0 && errno == EINTR);
|
|
if (ret == p && WIFEXITED (status) && WEXITSTATUS (status) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
struct sock_fprog seccomp_filter;
|
|
const char bpf[] = { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f };
|
|
seccomp_filter.len = 1;
|
|
seccomp_filter.filter = (struct sock_filter *) bpf;
|
|
|
|
if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
|
|
return 1;
|
|
|
|
ret = syscall_seccomp (SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, &seccomp_filter);
|
|
if (ret <= 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
else
|
|
error (EXIT_FAILURE, 0, "unknown feature");
|
|
}
|
|
|
|
error (EXIT_FAILURE, 0, "unknown command '%s' specified", argv[1]);
|
|
return 0;
|
|
}
|