Merge pull request #10996 from poettering/oci-prep
Preparation for the nspawn-OCI work
This commit is contained in:
+12
-10
@@ -10,12 +10,13 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "stat-util.h"
|
||||
|
||||
int block_get_whole_disk(dev_t d, dev_t *ret) {
|
||||
char p[SYS_BLOCK_PATH_MAX("/partition")];
|
||||
_cleanup_free_ char *s = NULL;
|
||||
unsigned n, m;
|
||||
dev_t devt;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
@@ -38,16 +39,16 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sscanf(s, "%u:%u", &m, &n);
|
||||
if (r != 2)
|
||||
return -EINVAL;
|
||||
r = parse_dev(s, &devt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Only return this if it is really good enough for us. */
|
||||
xsprintf_sys_block_path(p, "/queue", makedev(m, n));
|
||||
xsprintf_sys_block_path(p, "/queue", devt);
|
||||
if (access(p, F_OK) < 0)
|
||||
return -ENOENT;
|
||||
|
||||
*ret = makedev(m, n);
|
||||
*ret = devt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -85,8 +86,8 @@ int block_get_originating(dev_t dt, dev_t *ret) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
char p[SYS_BLOCK_PATH_MAX("/slaves")];
|
||||
struct dirent *de, *found = NULL;
|
||||
unsigned maj, min;
|
||||
const char *q;
|
||||
dev_t devt;
|
||||
int r;
|
||||
|
||||
/* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used,
|
||||
@@ -148,13 +149,14 @@ int block_get_originating(dev_t dt, dev_t *ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sscanf(t, "%u:%u", &maj, &min) != 2)
|
||||
r = parse_dev(t, &devt);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (maj == 0)
|
||||
if (major(devt) == 0)
|
||||
return -ENOENT;
|
||||
|
||||
*ret = makedev(maj, min);
|
||||
*ret = devt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -359,3 +359,128 @@ bool ambient_capabilities_supported(void) {
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
int capability_quintet_enforce(const CapabilityQuintet *q) {
|
||||
_cleanup_cap_free_ cap_t c = NULL;
|
||||
int r;
|
||||
|
||||
if (q->ambient != (uint64_t) -1) {
|
||||
unsigned long i;
|
||||
bool changed = false;
|
||||
|
||||
c = cap_get_proc();
|
||||
if (!c)
|
||||
return -errno;
|
||||
|
||||
/* In order to raise the ambient caps set we first need to raise the matching inheritable + permitted
|
||||
* cap */
|
||||
for (i = 0; i <= cap_last_cap(); i++) {
|
||||
uint64_t m = UINT64_C(1) << i;
|
||||
cap_value_t cv = (cap_value_t) i;
|
||||
cap_flag_value_t old_value_inheritable, old_value_permitted;
|
||||
|
||||
if ((q->ambient & m) == 0)
|
||||
continue;
|
||||
|
||||
if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value_inheritable) < 0)
|
||||
return -errno;
|
||||
if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value_permitted) < 0)
|
||||
return -errno;
|
||||
|
||||
if (old_value_inheritable == CAP_SET && old_value_permitted == CAP_SET)
|
||||
continue;
|
||||
|
||||
if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, CAP_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
if (cap_set_proc(c) < 0)
|
||||
return -errno;
|
||||
|
||||
r = capability_ambient_set_apply(q->ambient, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (q->inheritable != (uint64_t) -1 || q->permitted != (uint64_t) -1 || q->effective != (uint64_t) -1) {
|
||||
bool changed = false;
|
||||
unsigned long i;
|
||||
|
||||
if (!c) {
|
||||
c = cap_get_proc();
|
||||
if (!c)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (i = 0; i <= cap_last_cap(); i++) {
|
||||
uint64_t m = UINT64_C(1) << i;
|
||||
cap_value_t cv = (cap_value_t) i;
|
||||
|
||||
if (q->inheritable != (uint64_t) -1) {
|
||||
cap_flag_value_t old_value, new_value;
|
||||
|
||||
if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value) < 0)
|
||||
return -errno;
|
||||
|
||||
new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR;
|
||||
|
||||
if (old_value != new_value) {
|
||||
changed = true;
|
||||
|
||||
if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, new_value) < 0)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (q->permitted != (uint64_t) -1) {
|
||||
cap_flag_value_t old_value, new_value;
|
||||
|
||||
if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value) < 0)
|
||||
return -errno;
|
||||
|
||||
new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR;
|
||||
|
||||
if (old_value != new_value) {
|
||||
changed = true;
|
||||
|
||||
if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, new_value) < 0)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (q->effective != (uint64_t) -1) {
|
||||
cap_flag_value_t old_value, new_value;
|
||||
|
||||
if (cap_get_flag(c, cv, CAP_EFFECTIVE, &old_value) < 0)
|
||||
return -errno;
|
||||
|
||||
new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR;
|
||||
|
||||
if (old_value != new_value) {
|
||||
changed = true;
|
||||
|
||||
if (cap_set_flag(c, CAP_EFFECTIVE, 1, &cv, new_value) < 0)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
if (cap_set_proc(c) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (q->bounding != (uint64_t) -1) {
|
||||
r = capability_bounding_set_drop(q->bounding, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -43,3 +43,27 @@ bool ambient_capabilities_supported(void);
|
||||
/* Identical to linux/capability.h's CAP_TO_MASK(), but uses an unsigned 1U instead of a signed 1 for shifting left, in
|
||||
* order to avoid complaints about shifting a signed int left by 31 bits, which would make it negative. */
|
||||
#define CAP_TO_MASK_CORRECTED(x) (1U << ((x) & 31U))
|
||||
|
||||
typedef struct CapabilityQuintet {
|
||||
/* Stores all five types of capabilities in one go. Note that we use (uint64_t) -1 for unset here. This hence
|
||||
* needs to be updated as soon as Linux learns more than 63 caps. */
|
||||
uint64_t effective;
|
||||
uint64_t bounding;
|
||||
uint64_t inheritable;
|
||||
uint64_t permitted;
|
||||
uint64_t ambient;
|
||||
} CapabilityQuintet;
|
||||
|
||||
assert_cc(CAP_LAST_CAP < 64);
|
||||
|
||||
#define CAPABILITY_QUINTET_NULL { (uint64_t) -1, (uint64_t) -1, (uint64_t) -1, (uint64_t) -1, (uint64_t) -1 }
|
||||
|
||||
static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
|
||||
return q->effective != (uint64_t) -1 ||
|
||||
q->bounding != (uint64_t) -1 ||
|
||||
q->inheritable != (uint64_t) -1 ||
|
||||
q->permitted != (uint64_t) -1 ||
|
||||
q->ambient != (uint64_t) -1;
|
||||
}
|
||||
|
||||
int capability_quintet_enforce(const CapabilityQuintet *q);
|
||||
|
||||
+40
-10
@@ -211,31 +211,62 @@ int readlink_and_make_absolute(const char *p, char **r) {
|
||||
}
|
||||
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
|
||||
char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
|
||||
_cleanup_close_ int fd = -1;
|
||||
assert(path);
|
||||
|
||||
/* Under the assumption that we are running privileged we
|
||||
* first change the access mode and only then hand out
|
||||
/* Under the assumption that we are running privileged we first change the access mode and only then hand out
|
||||
* ownership to avoid a window where access is too open. */
|
||||
|
||||
if (mode != MODE_INVALID)
|
||||
if (chmod(path, mode) < 0)
|
||||
fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner
|
||||
* on the same file */
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
xsprintf(fd_path, "/proc/self/fd/%i", fd);
|
||||
|
||||
if (mode != MODE_INVALID) {
|
||||
|
||||
if ((mode & S_IFMT) != 0) {
|
||||
struct stat st;
|
||||
|
||||
if (stat(fd_path, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chmod(fd_path, mode & 07777) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (uid != UID_INVALID || gid != GID_INVALID)
|
||||
if (chown(path, uid, gid) < 0)
|
||||
if (chown(fd_path, uid, gid) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
||||
/* Under the assumption that we are running privileged we
|
||||
* first change the access mode and only then hand out
|
||||
/* Under the assumption that we are running privileged we first change the access mode and only then hand out
|
||||
* ownership to avoid a window where access is too open. */
|
||||
|
||||
if (mode != MODE_INVALID)
|
||||
if (fchmod(fd, mode) < 0)
|
||||
if (mode != MODE_INVALID) {
|
||||
|
||||
if ((mode & S_IFMT) != 0) {
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fchmod(fd, mode & 0777) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (uid != UID_INVALID || gid != GID_INVALID)
|
||||
if (fchown(fd, uid, gid) < 0)
|
||||
@@ -263,7 +294,6 @@ int fchmod_opath(int fd, mode_t m) {
|
||||
* fchownat() does. */
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
|
||||
if (chmod(procfs_path, m) < 0)
|
||||
return -errno;
|
||||
|
||||
|
||||
+20
-6
@@ -16,6 +16,7 @@
|
||||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int parse_boolean(const char *v) {
|
||||
@@ -731,17 +732,30 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
|
||||
}
|
||||
|
||||
int parse_dev(const char *s, dev_t *ret) {
|
||||
const char *major;
|
||||
unsigned x, y;
|
||||
dev_t d;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
if (sscanf(s, "%u:%u", &x, &y) != 2)
|
||||
n = strspn(s, DIGITS);
|
||||
if (n == 0)
|
||||
return -EINVAL;
|
||||
if (s[n] != ':')
|
||||
return -EINVAL;
|
||||
|
||||
d = makedev(x, y);
|
||||
if ((unsigned) major(d) != x || (unsigned) minor(d) != y)
|
||||
return -EINVAL;
|
||||
major = strndupa(s, n);
|
||||
r = safe_atou(major, &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = d;
|
||||
r = safe_atou(s + n + 1, &y);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
|
||||
return -ERANGE;
|
||||
|
||||
*ret = makedev(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+61
-11
@@ -481,18 +481,68 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
|
||||
return path_equal(a, b) || files_same(a, b, flags) > 0;
|
||||
}
|
||||
|
||||
char* path_join(const char *root, const char *path, const char *rest) {
|
||||
assert(path);
|
||||
char* path_join_many_internal(const char *first, ...) {
|
||||
char *joined, *q;
|
||||
const char *p;
|
||||
va_list ap;
|
||||
bool slash;
|
||||
size_t sz;
|
||||
|
||||
if (!isempty(root))
|
||||
return strjoin(root, endswith(root, "/") ? "" : "/",
|
||||
path[0] == '/' ? path+1 : path,
|
||||
rest ? (endswith(path, "/") ? "" : "/") : NULL,
|
||||
rest && rest[0] == '/' ? rest+1 : rest);
|
||||
else
|
||||
return strjoin(path,
|
||||
rest ? (endswith(path, "/") ? "" : "/") : NULL,
|
||||
rest && rest[0] == '/' ? rest+1 : rest);
|
||||
assert(first);
|
||||
|
||||
/* Joins all listed strings until NULL and places an "/" between them unless the strings end/begin already with
|
||||
* one so that it is unnecessary. Note that "/" which are already duplicate won't be removed. The string
|
||||
* returned is hence always equal or longer than the sum of the lengths of each individual string.
|
||||
*
|
||||
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
|
||||
* are optional.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* path_join_many("foo", "bar") → "foo/bar"
|
||||
* path_join_many("foo/", "bar") → "foo/bar"
|
||||
* path_join_many("", "foo", "", "bar", "") → "foo/bar" */
|
||||
|
||||
sz = strlen(first);
|
||||
va_start(ap, first);
|
||||
while ((p = va_arg(ap, char*))) {
|
||||
|
||||
if (*p == 0) /* Skip empty items */
|
||||
continue;
|
||||
|
||||
sz += 1 + strlen(p);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
joined = new(char, sz + 1);
|
||||
if (!joined)
|
||||
return NULL;
|
||||
|
||||
if (first[0] != 0) {
|
||||
q = stpcpy(joined, first);
|
||||
slash = endswith(first, "/");
|
||||
} else {
|
||||
/* Skip empty items */
|
||||
joined[0] = 0;
|
||||
q = joined;
|
||||
slash = true; /* no need to generate a slash anymore */
|
||||
}
|
||||
|
||||
va_start(ap, first);
|
||||
while ((p = va_arg(ap, char*))) {
|
||||
|
||||
if (*p == 0) /* Skip empty items */
|
||||
continue;
|
||||
|
||||
if (!slash && p[0] != '/')
|
||||
*(q++) = '/';
|
||||
|
||||
q = stpcpy(q, p);
|
||||
slash = endswith(p, "/");
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
int find_binary(const char *name, char **ret) {
|
||||
|
||||
@@ -49,7 +49,14 @@ char* path_startswith(const char *path, const char *prefix) _pure_;
|
||||
int path_compare(const char *a, const char *b) _pure_;
|
||||
bool path_equal(const char *a, const char *b) _pure_;
|
||||
bool path_equal_or_files_same(const char *a, const char *b, int flags);
|
||||
char* path_join(const char *root, const char *path, const char *rest);
|
||||
char* path_join_many_internal(const char *first, ...) _sentinel_;
|
||||
#define path_join_many(x, ...) path_join_many_internal(x, __VA_ARGS__, NULL)
|
||||
static inline char* path_join(const char *root, const char *path, const char *rest) {
|
||||
assert(path);
|
||||
|
||||
return path_join_many(strempty(root), path, rest);
|
||||
}
|
||||
|
||||
char* path_simplify(char *path, bool kill_dots);
|
||||
|
||||
static inline bool path_equal_ptr(const char *a, const char *b) {
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@@ -319,3 +321,99 @@ int fd_verify_directory(int fd) {
|
||||
|
||||
return stat_verify_directory(&st);
|
||||
}
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
|
||||
const char *t;
|
||||
|
||||
/* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
t = "char";
|
||||
else if (S_ISBLK(mode))
|
||||
t = "block";
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
/* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (major(devno) == 0 && minor(devno) == 0) {
|
||||
char *s;
|
||||
|
||||
/* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
|
||||
* /dev/block/ and /dev/char/, hence we handle them specially here. */
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
s = strdup("/run/systemd/inaccessible/chr");
|
||||
else if (S_ISBLK(mode))
|
||||
s = strdup("/run/systemd/inaccessible/blk");
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = device_path_make_major_minor(mode, devno, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return chase_symlinks(p, NULL, 0, ret);
|
||||
}
|
||||
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
|
||||
mode_t mode;
|
||||
dev_t devno;
|
||||
int r;
|
||||
|
||||
/* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
|
||||
* paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
|
||||
* path cannot be parsed like this. */
|
||||
|
||||
if (path_equal(path, "/run/systemd/inaccessible/chr")) {
|
||||
mode = S_IFCHR;
|
||||
devno = makedev(0, 0);
|
||||
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
|
||||
mode = S_IFBLK;
|
||||
devno = makedev(0, 0);
|
||||
} else {
|
||||
const char *w;
|
||||
|
||||
w = path_startswith(path, "/dev/block/");
|
||||
if (w)
|
||||
mode = S_IFBLK;
|
||||
else {
|
||||
w = path_startswith(path, "/dev/char/");
|
||||
if (!w)
|
||||
return -ENODEV;
|
||||
|
||||
mode = S_IFCHR;
|
||||
}
|
||||
|
||||
r = parse_dev(w, &devno);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_mode)
|
||||
*ret_mode = mode;
|
||||
if (ret_devno)
|
||||
*ret_devno = devno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,3 +62,26 @@ int fd_verify_regular(int fd);
|
||||
|
||||
int stat_verify_directory(const struct stat *st);
|
||||
int fd_verify_directory(int fd);
|
||||
|
||||
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
|
||||
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
|
||||
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
|
||||
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
|
||||
* such a test would be pointless in such a case.) */
|
||||
|
||||
#define DEVICE_MAJOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 12); \
|
||||
\
|
||||
})
|
||||
|
||||
#define DEVICE_MINOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 20); \
|
||||
})
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
|
||||
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
|
||||
|
||||
+38
-35
@@ -979,53 +979,56 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
|
||||
char fn[STRLEN("/dev/char/") + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
|
||||
_cleanup_free_ char *s = NULL;
|
||||
const char *p;
|
||||
int get_ctty(pid_t pid, dev_t *ret_devnr, char **ret) {
|
||||
_cleanup_free_ char *fn = NULL, *b = NULL;
|
||||
dev_t devnr;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
assert(r);
|
||||
r = get_ctty_devnr(pid, &devnr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
k = get_ctty_devnr(pid, &devnr);
|
||||
if (k < 0)
|
||||
return k;
|
||||
r = device_path_make_canonical(S_IFCHR, devnr, &fn);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT) /* No symlink for this in /dev/char/? */
|
||||
return r;
|
||||
|
||||
sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
|
||||
|
||||
k = readlink_malloc(fn, &s);
|
||||
if (k < 0) {
|
||||
|
||||
if (k != -ENOENT)
|
||||
return k;
|
||||
|
||||
/* This is an ugly hack */
|
||||
if (major(devnr) == 136) {
|
||||
/* This is an ugly hack: PTY devices are not listed in /dev/char/, as they don't follow the
|
||||
* Linux device model. This means we have no nice way to match them up against their actual
|
||||
* device node. Let's hence do the check by the fixed, assigned major number. Normally we try
|
||||
* to avoid such fixed major/minor matches, but there appears to nother nice way to handle
|
||||
* this. */
|
||||
|
||||
if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
/* Probably something like the ptys which have no
|
||||
* symlink in /dev/char. Let's return something
|
||||
* vaguely useful. */
|
||||
/* Probably something similar to the ptys which have no symlink in /dev/char/. Let's return
|
||||
* something vaguely useful. */
|
||||
|
||||
b = strdup(fn + 5);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
r = device_path_make_major_minor(S_IFCHR, devnr, &fn);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
p = PATH_STARTSWITH_SET(s, "/dev/", "../");
|
||||
if (!p)
|
||||
p = s;
|
||||
|
||||
b = strdup(p);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*r = b;
|
||||
if (_devnr)
|
||||
*_devnr = devnr;
|
||||
if (!b) {
|
||||
const char *w;
|
||||
|
||||
w = path_startswith(fn, "/dev/");
|
||||
if (w) {
|
||||
b = strdup(w);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
b = TAKE_PTR(fn);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(b);
|
||||
|
||||
if (ret_devnr)
|
||||
*ret_devnr = devnr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user