-
Notifications
You must be signed in to change notification settings - Fork 157
/
msg.c
150 lines (141 loc) · 4.22 KB
/
msg.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// SPDX-License-Identifier: MIT
#include <ctype.h> // need isdigit()
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // need strlen()
#include <unistd.h>
#include "msg.h"
// Print message, prefixed with "fatal: ", to stderr and exit with "code".
// Example: fatal(6, "could not compile regexp '%s'\n", regex_str);
void fatal(int code, char* fmt, ...)
{
char* red = "";
char* reset = "";
if (isatty(fileno(stderr))) {
red = "\033[31m";
reset = "\033[0m";
}
char fmt2[100];
snprintf(fmt2, sizeof(fmt2), "%sfatal: %s%s", red, fmt, reset);
va_list args;
va_start(args, fmt); // yes fmt, NOT fmt2!
vfprintf(stderr, fmt2, args);
va_end(args);
exit(code);
}
// Print a yellow warning message to stderr. No "warning" prefix is added.
int warn(const char* fmt, ...)
{
int ret = 0;
char* yellow = "";
char* reset = "";
if (isatty(fileno(stderr))) {
yellow = "\033[33m";
reset = "\033[0m";
}
char fmt2[100];
snprintf(fmt2, sizeof(fmt2), "%s%s%s", yellow, fmt, reset);
va_list args;
va_start(args, fmt); // yes fmt, NOT fmt2!
ret = vfprintf(stderr, fmt2, args);
va_end(args);
return ret;
}
// Parse the "123[,456]" tuple in optarg.
term_kill_tuple_t parse_term_kill_tuple(char* optarg, long upper_limit)
{
term_kill_tuple_t tuple = { 0 };
int n = 0;
// Arbitrary limit of 100 bytes to prevent snprintf truncation
if (strlen(optarg) > 100) {
snprintf(tuple.err, sizeof(tuple.err),
"argument too long (%d bytes)\n", (int)strlen(optarg));
return tuple;
}
for (size_t i = 0; i < strlen(optarg); i++) {
if (isdigit(optarg[i])) {
continue;
}
if (optarg[i] == ',') {
n++;
if (n == 1) {
continue;
}
snprintf(tuple.err, sizeof(tuple.err),
"found multiple ','\n");
return tuple;
}
snprintf(tuple.err, sizeof(tuple.err),
"found non-digit '%c'\n", optarg[i]);
return tuple;
}
n = sscanf(optarg, "%ld,%ld", &tuple.term, &tuple.kill);
if (n == 0) {
snprintf(tuple.err, sizeof(tuple.err),
"could not parse '%s'\n", optarg);
return tuple;
}
// User passed only the SIGTERM value: the SIGKILL value is calculated as
// SIGTERM/2.
if (n == 1) {
tuple.kill = tuple.term / 2;
}
// Would setting SIGTERM below SIGKILL ever make sense?
if (tuple.term < tuple.kill) {
warn("warning: SIGTERM value %ld is below SIGKILL value %ld, setting SIGTERM = SIGKILL = %ld\n",
tuple.term, tuple.kill, tuple.kill);
tuple.term = tuple.kill;
}
// Sanity checks
if (tuple.term < 0) {
snprintf(tuple.err, sizeof(tuple.err),
"negative SIGTERM value in '%s'\n", optarg);
return tuple;
}
if (tuple.term > upper_limit) {
snprintf(tuple.err, sizeof(tuple.err),
"SIGTERM value %ld exceeds limit %ld\n", tuple.term, upper_limit);
return tuple;
}
if (tuple.kill < 0) {
snprintf(tuple.err, sizeof(tuple.err),
"negative SIGKILL value in '%s'\n", optarg);
return tuple;
}
if (tuple.kill == 0 && tuple.term == 0) {
snprintf(tuple.err, sizeof(tuple.err),
"both SIGTERM and SIGKILL values are zero\n");
return tuple;
}
return tuple;
}
// Credit to https://gist.github.com/w-vi/67fe49106c62421992a2
// Only works for string of length 3 and up. This is good enough
// for our use case, which is fixing the 16-byte value we get
// from /proc/[pid]/comm.
//
// Tested in unit_test.go: Test_fix_truncated_utf8()
void fix_truncated_utf8(char *str) {
int len = strlen(str);
if (len < 3) {
return;
}
// We only need to look at the last three bytes
char *b = str + len - 3;
// Last byte is ascii
if ((b[2] & 0x80) == 0) {
return;
}
// Last byte is multi-byte sequence start
if (b[2] & 0x40) {
b[2] = 0;
}
// Truncated 3-byte sequence
else if ((b[1] & 0xe0) == 0xe0) {
b[1] = 0;
// Truncated 4-byte sequence
} else if ((b[0] & 0xf0) == 0xf0) {
b[0] = 0;
}
}