162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Helper functions for handling target threads/cpus
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012, LG Electronics, Namhyung Kim <namhyung.kim@lge.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "target.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <pwd.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <stdlib.h>
1362306a36Sopenharmony_ci#include <string.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cienum target_errno target__validate(struct target *target)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	enum target_errno ret = TARGET_ERRNO__SUCCESS;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (target->pid)
2262306a36Sopenharmony_ci		target->tid = target->pid;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/* CPU and PID are mutually exclusive */
2562306a36Sopenharmony_ci	if (target->tid && target->cpu_list) {
2662306a36Sopenharmony_ci		target->cpu_list = NULL;
2762306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
2862306a36Sopenharmony_ci			ret = TARGET_ERRNO__PID_OVERRIDE_CPU;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* UID and PID are mutually exclusive */
3262306a36Sopenharmony_ci	if (target->tid && target->uid_str) {
3362306a36Sopenharmony_ci		target->uid_str = NULL;
3462306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
3562306a36Sopenharmony_ci			ret = TARGET_ERRNO__PID_OVERRIDE_UID;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* UID and CPU are mutually exclusive */
3962306a36Sopenharmony_ci	if (target->uid_str && target->cpu_list) {
4062306a36Sopenharmony_ci		target->cpu_list = NULL;
4162306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
4262306a36Sopenharmony_ci			ret = TARGET_ERRNO__UID_OVERRIDE_CPU;
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* PID and SYSTEM are mutually exclusive */
4662306a36Sopenharmony_ci	if (target->tid && target->system_wide) {
4762306a36Sopenharmony_ci		target->system_wide = false;
4862306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
4962306a36Sopenharmony_ci			ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* UID and SYSTEM are mutually exclusive */
5362306a36Sopenharmony_ci	if (target->uid_str && target->system_wide) {
5462306a36Sopenharmony_ci		target->system_wide = false;
5562306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
5662306a36Sopenharmony_ci			ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* BPF and CPU are mutually exclusive */
6062306a36Sopenharmony_ci	if (target->bpf_str && target->cpu_list) {
6162306a36Sopenharmony_ci		target->cpu_list = NULL;
6262306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
6362306a36Sopenharmony_ci			ret = TARGET_ERRNO__BPF_OVERRIDE_CPU;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* BPF and PID/TID are mutually exclusive */
6762306a36Sopenharmony_ci	if (target->bpf_str && target->tid) {
6862306a36Sopenharmony_ci		target->tid = NULL;
6962306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
7062306a36Sopenharmony_ci			ret = TARGET_ERRNO__BPF_OVERRIDE_PID;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* BPF and UID are mutually exclusive */
7462306a36Sopenharmony_ci	if (target->bpf_str && target->uid_str) {
7562306a36Sopenharmony_ci		target->uid_str = NULL;
7662306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
7762306a36Sopenharmony_ci			ret = TARGET_ERRNO__BPF_OVERRIDE_UID;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* BPF and THREADS are mutually exclusive */
8162306a36Sopenharmony_ci	if (target->bpf_str && target->per_thread) {
8262306a36Sopenharmony_ci		target->per_thread = false;
8362306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
8462306a36Sopenharmony_ci			ret = TARGET_ERRNO__BPF_OVERRIDE_THREAD;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* THREAD and SYSTEM/CPU are mutually exclusive */
8862306a36Sopenharmony_ci	if (target->per_thread && (target->system_wide || target->cpu_list)) {
8962306a36Sopenharmony_ci		target->per_thread = false;
9062306a36Sopenharmony_ci		if (ret == TARGET_ERRNO__SUCCESS)
9162306a36Sopenharmony_ci			ret = TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return ret;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cienum target_errno target__parse_uid(struct target *target)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct passwd pwd, *result;
10062306a36Sopenharmony_ci	char buf[1024];
10162306a36Sopenharmony_ci	const char *str = target->uid_str;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	target->uid = UINT_MAX;
10462306a36Sopenharmony_ci	if (str == NULL)
10562306a36Sopenharmony_ci		return TARGET_ERRNO__SUCCESS;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Try user name first */
10862306a36Sopenharmony_ci	getpwnam_r(str, &pwd, buf, sizeof(buf), &result);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (result == NULL) {
11162306a36Sopenharmony_ci		/*
11262306a36Sopenharmony_ci		 * The user name not found. Maybe it's a UID number.
11362306a36Sopenharmony_ci		 */
11462306a36Sopenharmony_ci		char *endptr;
11562306a36Sopenharmony_ci		int uid = strtol(str, &endptr, 10);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		if (*endptr != '\0')
11862306a36Sopenharmony_ci			return TARGET_ERRNO__INVALID_UID;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		if (result == NULL)
12362306a36Sopenharmony_ci			return TARGET_ERRNO__USER_NOT_FOUND;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	target->uid = result->pw_uid;
12762306a36Sopenharmony_ci	return TARGET_ERRNO__SUCCESS;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * This must have a same ordering as the enum target_errno.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_cistatic const char *target__error_str[] = {
13462306a36Sopenharmony_ci	"PID/TID switch overriding CPU",
13562306a36Sopenharmony_ci	"PID/TID switch overriding UID",
13662306a36Sopenharmony_ci	"UID switch overriding CPU",
13762306a36Sopenharmony_ci	"PID/TID switch overriding SYSTEM",
13862306a36Sopenharmony_ci	"UID switch overriding SYSTEM",
13962306a36Sopenharmony_ci	"SYSTEM/CPU switch overriding PER-THREAD",
14062306a36Sopenharmony_ci	"BPF switch overriding CPU",
14162306a36Sopenharmony_ci	"BPF switch overriding PID/TID",
14262306a36Sopenharmony_ci	"BPF switch overriding UID",
14362306a36Sopenharmony_ci	"BPF switch overriding THREAD",
14462306a36Sopenharmony_ci	"Invalid User: %s",
14562306a36Sopenharmony_ci	"Problems obtaining information for user %s",
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciint target__strerror(struct target *target, int errnum,
14962306a36Sopenharmony_ci			  char *buf, size_t buflen)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int idx;
15262306a36Sopenharmony_ci	const char *msg;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	BUG_ON(buflen == 0);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (errnum >= 0) {
15762306a36Sopenharmony_ci		str_error_r(errnum, buf, buflen);
15862306a36Sopenharmony_ci		return 0;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (errnum <  __TARGET_ERRNO__START || errnum >= __TARGET_ERRNO__END)
16262306a36Sopenharmony_ci		return -1;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	idx = errnum - __TARGET_ERRNO__START;
16562306a36Sopenharmony_ci	msg = target__error_str[idx];
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	switch (errnum) {
16862306a36Sopenharmony_ci	case TARGET_ERRNO__PID_OVERRIDE_CPU ...
16962306a36Sopenharmony_ci	     TARGET_ERRNO__BPF_OVERRIDE_THREAD:
17062306a36Sopenharmony_ci		snprintf(buf, buflen, "%s", msg);
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	case TARGET_ERRNO__INVALID_UID:
17462306a36Sopenharmony_ci	case TARGET_ERRNO__USER_NOT_FOUND:
17562306a36Sopenharmony_ci		snprintf(buf, buflen, msg, target->uid_str);
17662306a36Sopenharmony_ci		break;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	default:
17962306a36Sopenharmony_ci		/* cannot reach here */
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
185