The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
radlock.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: f6335806ccebb34fe5343f03b82b12c0e00df258 $
19 *
20 * @file radlock.c
21 * @brief Utility to examine semaphores used to provide exclusive running rights for a process
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: f6335806ccebb34fe5343f03b82b12c0e00df258 $")
26
27#include <freeradius-devel/autoconf.h>
28#include <freeradius-devel/util/perm.h>
29#include <freeradius-devel/util/sem.h>
30#include <freeradius-devel/util/value.h>
31#include <freeradius-devel/util/syserror.h>
32
33#include <sys/ipc.h>
34#include <sys/sem.h>
35
36#ifdef HAVE_GETOPT_H
37# include <getopt.h>
38#endif
39#include <assert.h>
40
41DIAG_OFF(unused-macros)
42#define INFO(fmt, ...) fprintf(stdout, fmt "\n", ## __VA_ARGS__)
43DIAG_ON(unused-macros)
44
45typedef enum {
47 RADLOCK_LOCK, //!< Acquire the semaphore if it's at 0.
48 RADLOCK_TRYLOCK, //!< Try and lock the semaphore and return if we can't.
49 RADLOCK_UNLOCK, //!< Unlock the semaphore.
50 RADLOCK_REMOVE, //!< Remove the semaphore.
51 RADLOCK_INFO, //!< Information about the semaphore.
52 RADLOCK_PERM //!< Modify permissions for a given semaphore.
54
56 { L("info"), RADLOCK_INFO },
57 { L("lock"), RADLOCK_LOCK },
58 { L("perm"), RADLOCK_PERM },
59 { L("remove"), RADLOCK_REMOVE },
60 { L("trylock"), RADLOCK_TRYLOCK },
61 { L("unlock"), RADLOCK_UNLOCK }
62};
64
65static NEVER_RETURNS void usage(int ret)
66{
67 fprintf(stderr, "usage: radlock <file> [lock|trylock|unlock|remove|info|perm]\n");
68 fprintf(stderr, " -u <uid> Desired user.\n");
69 fprintf(stderr, " -g <gid> Desired group.\n");
70 fprintf(stderr, " -m <perm> Octal permissions string.\n");
71 fprintf(stderr, " -h This help text.\n");
72 fprintf(stderr, "\n");
73 fprintf(stderr, "Simple utility to query the locking state of a config file\n");
74 fr_exit_now(ret);
75}
76
77#define EXIT_WITH_FAILURE exit(EXIT_FAILURE)
78#define EXIT_WITH_SUCCESS exit(EXIT_SUCCESS)
79
80/**
81 *
82 * @hidecallgraph
83 */
84int main(int argc, char *argv[])
85{
86 int c;
88 char const *file;
89 char *end;
90 uid_t uid = geteuid();
91 bool uid_set = false;
92 gid_t gid = getegid();
93 bool gid_set = false;
94 long mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
95 bool mode_set = false;
96 int sem_id;
97
98 TALLOC_CTX *autofree;
99
101
102 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0], PANIC_ACTION_SIGNALS) < 0) {
103 fr_perror("radlock");
104 fr_exit(EXIT_FAILURE);
105 }
106
107 talloc_set_log_stderr();
108
109 while ((c = getopt(argc, argv, "u:g:m:h")) != -1) switch (c) {
110 case 'u':
111 if (fr_perm_uid_from_str(autofree, &uid, optarg) < 0) {
112 fr_perror("radlock");
114 }
115 uid_set = true;
116 break;
117
118 case 'g':
119 if (fr_perm_gid_from_str(autofree, &gid, optarg) < 0) {
120 fr_perror("radlock");
122 }
123 gid_set = true;
124 break;
125
126 case 'm':
127 mode = strtol(optarg, &end, 0); /* 0 base plus 0 prefix = octal */
128 if (*end || (end == optarg)) {
129 fr_perror("radlock - Bad mode value");
131 }
132 mode_set = true;
133 break;
134
135 case 'h':
136 default:
137 usage(EXIT_SUCCESS);
138 }
139 argc -= optind;
140 argv += optind;
141
142 if (argc == 0) {
143 fr_perror("radlock - Need file to operate on");
144 usage(64);
145 }
146
147 if (argc == 1) {
148 fr_perror("radlock - Need action, must be one of (lock|trylock|unlock|remove|info|perm)");
149 usage(64);
150 }
151
152 file = argv[0];
154 if (action == RADLOCK_INVALID) {
155 fr_perror("radlock - Action must be one of (lock|trylock|unlock|remove|info|perm), got %s", argv[1]);
156 usage(64);
157 }
158
159 if ((action == RADLOCK_PERM) && !uid_set && !gid_set && !mode_set) {
160 fr_perror("radlock - At least one of -u, -g, -m must be specified");
161 usage(64);
162 }
163
164 /*
165 * Mismatch between the binary and the libraries it depends on
166 */
168 fr_perror("radlock");
170 }
171
172 switch (action) {
173 case RADLOCK_LOCK:
174 case RADLOCK_TRYLOCK:
175 sem_id = fr_sem_get(file, 0, uid, gid, false, false);
176 if (sem_id < 0) {
177 fr_perror("radlock");
179 }
180 switch (fr_sem_wait(sem_id, file, false, action == RADLOCK_TRYLOCK)) {
181 case 1: /* Already locked */
182 {
183 pid_t pid;
184
185 fr_sem_pid(&pid, sem_id);
186 fr_perror("radlock - Can't lock \"%s\" already held by PID %u", file, pid);
188 }
189
190 case 0:
192
193 default:
194 break;
195 }
196 fr_perror("radlock");
198
199 case RADLOCK_UNLOCK:
200 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
201 if (sem_id == -4) EXIT_WITH_SUCCESS;
202 if (sem_id < 0) {
203 fr_perror("radlock");
205 }
206
207 again:
208 switch (fr_sem_post(sem_id, file, false)) {
209 case 1: /* already unlocked */
211
212 case 0:
213 goto again;
214
215 default:
216 break;
217 }
218 fr_perror("radlock");
220
221 case RADLOCK_REMOVE:
222 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
223 if (sem_id == -4) EXIT_WITH_SUCCESS;
224 if (sem_id < 0) {
225 fr_perror("radlock");
227 }
228
229 if (fr_sem_close(sem_id, file) < 0) {
230 fr_perror("radlock");
232 }
234
235 case RADLOCK_INFO:
236 {
237 struct semid_ds info;
238 char buff[10];
239 unsigned int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
240 bool dead = false;
241 pid_t pid;
242 int ret;
243 int value;
244 char const *uid_str, *gid_str, *cuid_str, *cgid_str;
245
246 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
247 if (sem_id == -4) EXIT_WITH_FAILURE;
248 if (sem_id < 0) {
249 fr_perror("radlock");
251 }
252
253 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
254 fr_perror("radlock - Failed getting lock info for \"%s\": %s", file, fr_syserror(errno));
256 }
257
258 if (fr_sem_pid(&pid, sem_id) < 0) {
259 fr_perror("radlock");
261 }
262
263 ret = kill(pid, 0);
264 if ((ret < 0) && (errno == ESRCH)) dead = true;
265
266 uid_str = fr_perm_uid_to_str(autofree, info.sem_perm.uid);
267 if (!uid_str) uid_str = "";
268
269 gid_str = fr_perm_gid_to_str(autofree, info.sem_perm.gid);
270 if (!gid_str) gid_str = "";
271
272 cuid_str = fr_perm_uid_to_str(autofree, info.sem_perm.cuid);
273 if (!cuid_str) cuid_str = "";
274
275 cgid_str = fr_perm_gid_to_str(autofree, info.sem_perm.cgid);
276 if (!cgid_str) cgid_str = "";
277
278 value = semctl(sem_id, 0, GETVAL);
279
280 INFO("Locking information for \"%s\"", file);
281 INFO("\tsemid : %u", sem_id);
282 INFO("\tPermissions : %s", fr_perm_mode_to_str(buff, info.sem_perm.mode & perm));
283 INFO("\tValue : %u (%s)", value, value > 0 ? "locked" : "unlocked");
284 INFO("Last Modified:");
285 INFO("\tPID : %u (%s)", pid, dead ? "dead" : "alive");
286 INFO("\tUser : %s (%u)", uid_str, info.sem_perm.uid);
287 INFO("\tGroup : %s (%u)", gid_str, info.sem_perm.gid);
288 INFO("\tTime : %s",
290 INFO("Created:");
291 INFO("\tUser : %s (%u)", cuid_str, info.sem_perm.cuid);
292 INFO("\tGroup : %s (%u)", cgid_str, info.sem_perm.cgid);
293 INFO("\tTime : %s",
295 }
297
298 case RADLOCK_PERM:
299 {
300 struct semid_ds info;
301
302 sem_id = fr_sem_get(file, 0, uid, gid, false, false); /* Will create if does not already exist */
303 if (sem_id < 0) {
304 fr_perror("radlock");
306 }
307
308 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
309 fr_perror("radlock - Failed getting lock info for \"%s\": %s",
310 file, fr_syserror(errno));
312 }
313
314 if (uid_set) info.sem_perm.uid = uid;
315 if (gid_set) info.sem_perm.gid = gid;
316 if (mode_set) info.sem_perm.mode = mode;
317
318 if (semctl(sem_id, 0, IPC_SET, &info) < 0) {
319 fr_perror("radlock - Failed setting lock permissions for \"%s\": %s",
320 file, fr_syserror(errno));
322 }
323 }
325
326 case RADLOCK_INVALID:
327 usage(64);
328 }
329
330 return 0;
331}
int const char * file
Definition acutest.h:702
#define RCSID(id)
Definition build.h:560
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:382
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define DIAG_ON(_x)
Definition build.h:535
#define NUM_ELEMENTS(_t)
Definition build.h:406
#define DIAG_OFF(_x)
Definition build.h:534
TALLOC_CTX * autofree
Definition common.c:29
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program, unsigned long fault_signals)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1074
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:261
#define PANIC_ACTION_SIGNALS
Definition debug.h:128
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:267
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
Test enumeration values.
Definition dict_test.h:92
char * fr_perm_uid_to_str(TALLOC_CTX *ctx, uid_t uid)
Print uid to a string.
Definition perm.c:496
char const * fr_perm_mode_to_str(char out[static 10], mode_t mode)
Convert mode_t into humanly readable permissions flags.
Definition perm.c:36
char * fr_perm_gid_to_str(TALLOC_CTX *ctx, uid_t gid)
Print gid to a string.
Definition perm.c:516
int fr_perm_uid_from_str(TALLOC_CTX *ctx, uid_t *out, char const *name)
Resolve a user name to a GID.
Definition perm.c:453
int fr_perm_gid_from_str(TALLOC_CTX *ctx, gid_t *out, char const *name)
Resolve a group name to a GID.
Definition perm.c:475
fr_radlock_action_t
Definition radlock.c:45
@ RADLOCK_TRYLOCK
Try and lock the semaphore and return if we can't.
Definition radlock.c:48
@ RADLOCK_UNLOCK
Unlock the semaphore.
Definition radlock.c:49
@ RADLOCK_PERM
Modify permissions for a given semaphore.
Definition radlock.c:52
@ RADLOCK_LOCK
Acquire the semaphore if it's at 0.
Definition radlock.c:47
@ RADLOCK_INVALID
Definition radlock.c:46
@ RADLOCK_REMOVE
Remove the semaphore.
Definition radlock.c:50
@ RADLOCK_INFO
Information about the semaphore.
Definition radlock.c:51
int main(int argc, char *argv[])
Definition radlock.c:84
static fr_table_num_sorted_t const radlock_action_table[]
Definition radlock.c:55
#define EXIT_WITH_SUCCESS
Definition radlock.c:78
#define EXIT_WITH_FAILURE
Definition radlock.c:77
#define INFO(fmt,...)
Definition radlock.c:42
static size_t radlock_action_table_len
Definition radlock.c:63
int fr_sem_post(int sem_id, char const *file, bool undo_on_exit)
Decrement the semaphore by 1.
Definition sem.c:181
int fr_sem_wait(int sem_id, char const *file, bool undo_on_exit, bool nonblock)
Wait for a semaphore to reach 0, then increment it by 1.
Definition sem.c:251
int fr_sem_close(int sem_id, char const *file)
Remove the semaphore, this helps with permissions issues.
Definition sem.c:328
int fr_sem_pid(pid_t *pid, int sem_id)
Return the PID of the process that last operated on the semaphore.
Definition sem.c:52
int fr_sem_get(char const *file, int proj_id, uid_t uid, gid_t gid, bool check_perm, bool must_exist)
Returns a semid for the semaphore associated with the file.
Definition sem.c:420
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:55
Definition testlib.h:54
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
Definition time.h:858
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:883
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
#define fr_box_time(_val)
Definition value.h:349