initctl: refactor, split into multiple files

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2021-03-16 09:23:04 +01:00
parent 08144df5ea
commit 74a26f559d
6 changed files with 469 additions and 354 deletions
+3 -3
View File
@@ -59,9 +59,9 @@ else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c client.c client.h \
reboot.c serv.c serv.h svc.h \
cond.c cond.h util.c util.h
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
client.c client.h cond.c cond.h reboot.c \
serv.c serv.h svc.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS)
+382
View File
@@ -0,0 +1,382 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <err.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <inttypes.h>
#include <lite/lite.h>
#include "initctl.h"
#define NONE " "
#define PIPE plain ? "|" : "│"
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
struct cg {
struct cg *cg_next;
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
float cg_load; /* curr - prev / 10000000.0 */
};
struct cg *list;
static char *pid_cmdline(int pid, char *buf, size_t len)
{
size_t i, sz;
char *ptr;
FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) {
buf[0] = 0;
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
fclose(fp);
if (!sz)
return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
}
for (i = 0; i < sz; i++) {
if (buf[i] == 0)
buf[i] = ' ';
}
return buf;
}
static char *pid_comm(int pid, char *buf, size_t len)
{
FILE *fp;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
fgets(buf, len, fp);
fclose(fp);
return chomp(buf);
}
static uint64_t cgroup_uint64(char *path, char *file)
{
uint64_t val = 0;
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (!fp)
return val;
if (fgets(buf, sizeof(buf), fp))
val = strtoull(buf, NULL, 10);
fclose(fp);
return val;
}
static int cgroup_shares(char *path)
{
return (int)cgroup_uint64(path, "cpu.shares");
}
static char *cgroup_memuse(char *path)
{
static char buf[32];
int gb, mb, kb, b;
uint64_t num;
num = cgroup_uint64(path, "memory.usage_in_bytes");
gb = num / (1024 * 1024 * 1024);
num = num % (1024 * 1024 * 1024);
mb = num / (1024 * 1024);
num = num % (1024 * 1024);
kb = num / (1024);
b = num % (1024);
if (gb)
snprintf(buf, sizeof(buf), "%d.%dG", gb, mb / 102);
else if (mb)
snprintf(buf, sizeof(buf), "%d.%dM", mb, kb / 102);
else
snprintf(buf, sizeof(buf), "%d.%dk", kb, b / 102);
return buf;
}
static struct cg *append(char *path)
{
struct cg *cg;
char fn[256];
ENTRY item;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
if (access(fn, F_OK)) {
warn("not a cgroup path with cpuacct controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
cg->cg_next = list;
list = cg;
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
return cg;
}
static struct cg *find(char *path)
{
ENTRY *ep, item = { path, NULL };
ep = hsearch(item, FIND);
if (ep)
return ep->data;
return append(path);
}
static float cgroup_cpuload(char *path)
{
struct cg *cg;
char fn[256];
char buf[64];
FILE *fp;
cg = find(path);
if (!cg)
return 0.0;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
if (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
curr = strtoull(buf, NULL, 10);
if (cg->cg_prev != 0) {
uint64_t diff = curr - cg->cg_prev;
/* this expects 1 sec poll interval */
cg->cg_load = (float)(diff / 1000000);
cg->cg_load /= 10.0;
}
cg->cg_prev = curr;
}
fclose(fp);
return cg->cg_load;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
#define CDIM (plain ? "" : "\e[2m")
#define CRST (plain ? "" : "\e[0m")
static int dump_cgroup(char *path, char *pfx, int top)
{
struct dirent **namelist = NULL;
struct stat st;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (top >= screen_rows)
return 0;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
if (top)
printf(" %6.6s %5.1f [%s]\n", cgroup_memuse(path),
cgroup_cpuload(path), path);
else
puts(path);
}
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (top)
printf(" %6.6s %5.1f ", cgroup_memuse(buf),
cgroup_cpuload(buf));
if (i + 1 == n) {
printf("%s%s ", pfx, END);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
printf("%s%s ", pfx, FORK);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
}
printf("%s/ [cpu.shares: %d]\n", nm, cgroup_shares(buf));
rc += dump_cgroup(buf, prefix, top + i);
free(namelist[i]);
}
free(namelist);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 };
pid_t pid;
pid = atoi(chomp(buf));
if (pid <= 0)
continue;
/* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf)))
printf("%s%s%s %s%d %s %s%s\n",
top ? " " : "",
pfx, ++i == num ? END : FORK,
CDIM, pid, comm, buf, CRST);
top += i;
if (top >= screen_rows)
break;
}
return fclose(fp);
}
fclose(fp);
return rc;
}
int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return dump_cgroup(arg, NULL, 0);
}
int show_cgtop(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
if (!hcreate(screen_rows + 25))
err(1, "failed creating hash table");
while (1) {
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" MEMORY %%CPU GROUP/SERVICE");
dump_cgroup(arg, NULL, 1);
sleep(1);
}
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+37
View File
@@ -0,0 +1,37 @@
/* Cgroup utility functions for initctl
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_CGUTIL_H_
#define FINIT_CGUTIL_H_
int show_cgroup (char *arg);
int show_cgtop (char *arg);
#endif /* FINIT_CGUTIL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -343
View File
@@ -26,13 +26,10 @@
#include <err.h>
#include <ftw.h>
#include <ctype.h>
#include <dirent.h>
#include <getopt.h>
#include <paths.h>
#include <search.h>
#include <signal.h>
#include <stdio.h>
#include <inttypes.h>
#include <time.h>
#include <utmp.h>
#include <arpa/inet.h>
@@ -42,13 +39,9 @@
#include "cond.h"
#include "serv.h"
#include "service.h"
#include "cgutil.h"
#include "util.h"
#define NONE " "
#define PIPE plain ? "|" : "│"
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
struct cmd {
char *cmd;
struct cmd *ctx;
@@ -677,341 +670,6 @@ static int show_status(char *arg)
return 0;
}
static char *pid_cmdline(int pid, char *buf, size_t len)
{
size_t i, sz;
char *ptr;
FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) {
buf[0] = 0;
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
fclose(fp);
if (!sz)
return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
}
for (i = 0; i < sz; i++) {
if (buf[i] == 0)
buf[i] = ' ';
}
return buf;
}
static char *pid_comm(int pid, char *buf, size_t len)
{
FILE *fp;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
fgets(buf, len, fp);
fclose(fp);
return chomp(buf);
}
static uint64_t cgroup_uint64(char *path, char *file)
{
uint64_t val = 0;
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (!fp)
return val;
if (fgets(buf, sizeof(buf), fp))
val = strtoull(buf, NULL, 10);
fclose(fp);
return val;
}
static int cgroup_shares(char *path)
{
return (int)cgroup_uint64(path, "cpu.shares");
}
static char *cgroup_memuse(char *path)
{
static char buf[32];
int gb, mb, kb, b;
uint64_t num;
num = cgroup_uint64(path, "memory.usage_in_bytes");
gb = num / (1024 * 1024 * 1024);
num = num % (1024 * 1024 * 1024);
mb = num / (1024 * 1024);
num = num % (1024 * 1024);
kb = num / (1024);
b = num % (1024);
if (gb)
snprintf(buf, sizeof(buf), "%d.%dG", gb, mb / 102);
else if (mb)
snprintf(buf, sizeof(buf), "%d.%dM", mb, kb / 102);
else
snprintf(buf, sizeof(buf), "%d.%dk", kb, b / 102);
return buf;
}
struct cg {
struct cg *cg_next;
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
float cg_load; /* curr - prev / 10000000.0 */
};
struct cg *list;
static struct cg *append(char *path)
{
struct cg *cg;
char fn[256];
ENTRY item;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
if (access(fn, F_OK)) {
warn("not a cgroup path with cpuacct controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
cg->cg_next = list;
list = cg;
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
return cg;
}
static struct cg *find(char *path)
{
ENTRY *ep, item = { path, NULL };
ep = hsearch(item, FIND);
if (ep)
return ep->data;
return append(path);
}
static float cgroup_cpuload(char *path)
{
struct cg *cg;
char fn[256];
char buf[64];
FILE *fp;
cg = find(path);
if (!cg)
return 0.0;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
if (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
curr = strtoull(buf, NULL, 10);
if (cg->cg_prev != 0) {
uint64_t diff = curr - cg->cg_prev;
/* this expects 1 sec poll interval */
cg->cg_load = (float)(diff / 1000000);
cg->cg_load /= 10.0;
}
cg->cg_prev = curr;
}
fclose(fp);
return cg->cg_load;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
#define CDIM (plain ? "" : "\e[2m")
#define CRST (plain ? "" : "\e[0m")
static int dump_cgroup(char *path, char *pfx, int top)
{
struct dirent **namelist = NULL;
struct stat st;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (top >= screen_rows)
return 0;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
if (top)
printf(" %6.6s %5.1f [%s]\n", cgroup_memuse(path),
cgroup_cpuload(path), path);
else
puts(path);
}
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (top)
printf(" %6.6s %5.1f ", cgroup_memuse(buf),
cgroup_cpuload(buf));
if (i + 1 == n) {
printf("%s%s ", pfx, END);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
printf("%s%s ", pfx, FORK);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
}
printf("%s/ [cpu.shares: %d]\n", nm, cgroup_shares(buf));
rc += dump_cgroup(buf, prefix, top + i);
free(namelist[i]);
}
free(namelist);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 };
pid_t pid;
pid = atoi(chomp(buf));
if (pid <= 0)
continue;
/* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf)))
printf("%s%s%s %s%d %s %s%s\n",
top ? " " : "",
pfx, ++i == num ? END : FORK,
CDIM, pid, comm, buf, CRST);
top += i;
if (top >= screen_rows)
break;
}
return fclose(fp);
}
fclose(fp);
return rc;
}
static int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return dump_cgroup(arg, NULL, 0);
}
static int show_cgtop(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
if (!hcreate(screen_rows + 25))
err(1, "failed creating hash table");
while (1) {
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" MEMORY %%CPU GROUP/SERVICE");
dump_cgroup(arg, NULL, 1);
sleep(1);
}
return 0;
}
static int transform(char *nm)
{
char *names[] = {
+45
View File
@@ -0,0 +1,45 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_INITCTL_H_
#define FINIT_INITCTL_H_
#include "finit.h"
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
extern int heading; /* initctl -t */
extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */
extern void print_header(const char *fmt, ...);
#endif /* FINIT_INITCTL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -8
View File
@@ -30,14 +30,7 @@
#include <stdlib.h>
#include <lite/lite.h>
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
extern int plain; /* initctl -p */
extern int heading; /* initctl -t */
extern void print_header(const char *fmt, ...);
#include "initctl.h"
static int calc_width(char *arr[], size_t len)