iproute2/lib/fs.c
<<
>>
Prefs
   1/*
   2 * fs.c         filesystem APIs
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     David Ahern <dsa@cumulusnetworks.com>
  10 *
  11 */
  12
  13#include <sys/types.h>
  14#include <sys/stat.h>
  15#include <sys/socket.h>
  16#include <sys/mount.h>
  17#include <ctype.h>
  18#include <fcntl.h>
  19#include <stdio.h>
  20#include <stdlib.h>
  21#include <unistd.h>
  22#include <string.h>
  23#include <errno.h>
  24#include <limits.h>
  25
  26#include "utils.h"
  27
  28#ifndef HAVE_HANDLE_AT
  29# include <sys/syscall.h>
  30#endif
  31
  32#define CGROUP2_FS_NAME "cgroup2"
  33
  34/* if not already mounted cgroup2 is mounted here for iproute2's use */
  35#define MNT_CGRP2_PATH  "/var/run/cgroup2"
  36
  37
  38#ifndef HAVE_HANDLE_AT
  39struct file_handle {
  40        unsigned handle_bytes;
  41        int handle_type;
  42        unsigned char f_handle[];
  43};
  44
  45static int name_to_handle_at(int dirfd, const char *pathname,
  46        struct file_handle *handle, int *mount_id, int flags)
  47{
  48        return syscall(__NR_name_to_handle_at, dirfd, pathname, handle,
  49                       mount_id, flags);
  50}
  51
  52static int open_by_handle_at(int mount_fd, struct file_handle *handle, int flags)
  53{
  54        return syscall(__NR_open_by_handle_at, mount_fd, handle, flags);
  55}
  56#endif
  57
  58/* return mount path of first occurrence of given fstype */
  59static char *find_fs_mount(const char *fs_to_find)
  60{
  61        char path[4096];
  62        char fstype[128];    /* max length of any filesystem name */
  63        char *mnt = NULL;
  64        FILE *fp;
  65
  66        fp = fopen("/proc/mounts", "r");
  67        if (!fp) {
  68                fprintf(stderr,
  69                        "Failed to open mounts file: %s\n", strerror(errno));
  70                return NULL;
  71        }
  72
  73        while (fscanf(fp, "%*s %4095s %127s %*s %*d %*d\n",
  74                      path, fstype) == 2) {
  75                if (strcmp(fstype, fs_to_find) == 0) {
  76                        mnt = strdup(path);
  77                        break;
  78                }
  79        }
  80
  81        fclose(fp);
  82
  83        return mnt;
  84}
  85
  86/* caller needs to free string returned */
  87char *find_cgroup2_mount(bool do_mount)
  88{
  89        char *mnt = find_fs_mount(CGROUP2_FS_NAME);
  90
  91        if (mnt)
  92                return mnt;
  93
  94        if (!do_mount) {
  95                fprintf(stderr, "Failed to find cgroup2 mount\n");
  96                return NULL;
  97        }
  98
  99        mnt = strdup(MNT_CGRP2_PATH);
 100        if (!mnt) {
 101                fprintf(stderr, "Failed to allocate memory for cgroup2 path\n");
 102                return NULL;
 103
 104        }
 105
 106        if (make_path(mnt, 0755)) {
 107                fprintf(stderr, "Failed to setup cgroup2 directory\n");
 108                free(mnt);
 109                return NULL;
 110        }
 111
 112        if (mount("none", mnt, CGROUP2_FS_NAME, 0, NULL)) {
 113                /* EBUSY means already mounted */
 114                if (errno == EBUSY)
 115                        goto out;
 116
 117                if (errno == ENODEV) {
 118                        fprintf(stderr,
 119                                "Failed to mount cgroup2. Are CGROUPS enabled in your kernel?\n");
 120                } else {
 121                        fprintf(stderr,
 122                                "Failed to mount cgroup2: %s\n",
 123                                strerror(errno));
 124                }
 125                free(mnt);
 126                return NULL;
 127        }
 128out:
 129        return mnt;
 130}
 131
 132__u64 get_cgroup2_id(const char *path)
 133{
 134        char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
 135        struct file_handle *fhp = (struct file_handle *)fh_buf;
 136        union {
 137                __u64 id;
 138                unsigned char bytes[sizeof(__u64)];
 139        } cg_id = { .id = 0 };
 140        char *mnt = NULL;
 141        int mnt_fd = -1;
 142        int mnt_id;
 143
 144        if (!path) {
 145                fprintf(stderr, "Invalid cgroup2 path\n");
 146                return 0;
 147        }
 148
 149        fhp->handle_bytes = sizeof(__u64);
 150        if (name_to_handle_at(AT_FDCWD, path, fhp, &mnt_id, 0) < 0) {
 151                /* try at cgroup2 mount */
 152
 153                while (*path == '/')
 154                        path++;
 155                if (*path == '\0') {
 156                        fprintf(stderr, "Invalid cgroup2 path\n");
 157                        goto out;
 158                }
 159
 160                mnt = find_cgroup2_mount(false);
 161                if (!mnt)
 162                        goto out;
 163
 164                mnt_fd = open(mnt, O_RDONLY);
 165                if (mnt_fd < 0) {
 166                        fprintf(stderr, "Failed to open cgroup2 mount\n");
 167                        goto out;
 168                }
 169
 170                fhp->handle_bytes = sizeof(__u64);
 171                if (name_to_handle_at(mnt_fd, path, fhp, &mnt_id, 0) < 0) {
 172                        fprintf(stderr, "Failed to get cgroup2 ID: %s\n",
 173                                        strerror(errno));
 174                        goto out;
 175                }
 176        }
 177        if (fhp->handle_bytes != sizeof(__u64)) {
 178                fprintf(stderr, "Invalid size of cgroup2 ID\n");
 179                goto out;
 180        }
 181
 182        memcpy(cg_id.bytes, fhp->f_handle, sizeof(__u64));
 183
 184out:
 185        if (mnt_fd >= 0)
 186                close(mnt_fd);
 187        free(mnt);
 188
 189        return cg_id.id;
 190}
 191
 192#define FILEID_INO32_GEN 1
 193
 194/* caller needs to free string returned */
 195char *get_cgroup2_path(__u64 id, bool full)
 196{
 197        char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
 198        struct file_handle *fhp = (struct file_handle *)fh_buf;
 199        union {
 200                __u64 id;
 201                unsigned char bytes[sizeof(__u64)];
 202        } cg_id = { .id = id };
 203        int mnt_fd = -1, fd = -1;
 204        char link_buf[PATH_MAX];
 205        char *path = NULL;
 206        char fd_path[64];
 207        int link_len;
 208        char *mnt = NULL;
 209
 210        if (!id) {
 211                fprintf(stderr, "Invalid cgroup2 ID\n");
 212                goto out;
 213        }
 214
 215        mnt = find_cgroup2_mount(false);
 216        if (!mnt)
 217                goto out;
 218
 219        mnt_fd = open(mnt, O_RDONLY);
 220        if (mnt_fd < 0) {
 221                fprintf(stderr, "Failed to open cgroup2 mount\n");
 222                goto out;
 223        }
 224
 225        fhp->handle_bytes = sizeof(__u64);
 226        fhp->handle_type = FILEID_INO32_GEN;
 227        memcpy(fhp->f_handle, cg_id.bytes, sizeof(__u64));
 228
 229        fd = open_by_handle_at(mnt_fd, fhp, 0);
 230        if (fd < 0) {
 231                fprintf(stderr, "Failed to open cgroup2 by ID\n");
 232                goto out;
 233        }
 234
 235        snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
 236        link_len = readlink(fd_path, link_buf, sizeof(link_buf) - 1);
 237        if (link_len < 0) {
 238                fprintf(stderr,
 239                        "Failed to read value of symbolic link %s\n",
 240                        fd_path);
 241                goto out;
 242        }
 243        link_buf[link_len] = '\0';
 244
 245        if (full)
 246                path = strdup(link_buf);
 247        else
 248                path = strdup(link_buf + strlen(mnt));
 249        if (!path)
 250                fprintf(stderr,
 251                        "Failed to allocate memory for cgroup2 path\n");
 252
 253out:
 254        if (fd >= 0)
 255                close(fd);
 256        if (mnt_fd >= 0)
 257                close(mnt_fd);
 258        free(mnt);
 259
 260        return path;
 261}
 262
 263int make_path(const char *path, mode_t mode)
 264{
 265        char *dir, *delim;
 266        int rc = -1;
 267
 268        delim = dir = strdup(path);
 269        if (dir == NULL) {
 270                fprintf(stderr, "strdup failed copying path");
 271                return -1;
 272        }
 273
 274        /* skip '/' -- it had better exist */
 275        if (*delim == '/')
 276                delim++;
 277
 278        while (1) {
 279                delim = strchr(delim, '/');
 280                if (delim)
 281                        *delim = '\0';
 282
 283                rc = mkdir(dir, mode);
 284                if (rc && errno != EEXIST) {
 285                        fprintf(stderr, "mkdir failed for %s: %s\n",
 286                                dir, strerror(errno));
 287                        goto out;
 288                }
 289
 290                if (delim == NULL)
 291                        break;
 292
 293                *delim = '/';
 294                delim++;
 295                if (*delim == '\0')
 296                        break;
 297        }
 298        rc = 0;
 299out:
 300        free(dir);
 301
 302        return rc;
 303}
 304
 305int get_command_name(const char *pid, char *comm, size_t len)
 306{
 307        char path[PATH_MAX];
 308        char line[128];
 309        FILE *fp;
 310
 311        if (snprintf(path, sizeof(path),
 312                     "/proc/%s/status", pid) >= sizeof(path)) {
 313                return -1;
 314        }
 315
 316        fp = fopen(path, "r");
 317        if (!fp)
 318                return -1;
 319
 320        comm[0] = '\0';
 321        while (fgets(line, sizeof(line), fp)) {
 322                char *nl, *name;
 323
 324                name = strstr(line, "Name:");
 325                if (!name)
 326                        continue;
 327
 328                name += 5;
 329                while (isspace(*name))
 330                        name++;
 331
 332                nl = strchr(name, '\n');
 333                if (nl)
 334                        *nl = '\0';
 335
 336                strlcpy(comm, name, len);
 337                break;
 338        }
 339
 340        fclose(fp);
 341
 342        return 0;
 343}
 344
 345char *get_task_name(pid_t pid)
 346{
 347        char *comm;
 348        FILE *f;
 349
 350        if (!pid)
 351                return NULL;
 352
 353        if (asprintf(&comm, "/proc/%d/comm", pid) < 0)
 354                return NULL;
 355
 356        f = fopen(comm, "r");
 357        if (!f)
 358                return NULL;
 359
 360        if (fscanf(f, "%ms\n", &comm) != 1)
 361                comm = NULL;
 362
 363        fclose(f);
 364
 365        return comm;
 366}
 367