Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/tests/lib/rcscripts.c
5238 views
1
/*-
2
* Copyright (c) 2026 Baptiste Daroussin <[email protected]>
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
#include <sys/stat.h>
8
9
#include <atf-c.h>
10
#include <fcntl.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <unistd.h>
15
16
#include <private/pkg.h>
17
#include <xmalloc.h>
18
#include <pkghash.h>
19
20
ATF_TC_WITHOUT_HEAD(deferred_rc_init_free);
21
ATF_TC_WITHOUT_HEAD(deferred_rc_free_null);
22
ATF_TC_WITHOUT_HEAD(deferred_rc_stop_entries);
23
ATF_TC_WITHOUT_HEAD(deferred_rc_start_entries);
24
ATF_TC_WITHOUT_HEAD(deferred_rc_dedup);
25
ATF_TC_WITHOUT_HEAD(deferred_rc_tmpdir_cleanup);
26
ATF_TC_WITHOUT_HEAD(deferred_rc_free_reuse);
27
ATF_TC_WITHOUT_HEAD(deferred_rc_stop_all_null_oldpath);
28
ATF_TC_WITHOUT_HEAD(deferred_rc_tmpdir_multiple_scripts);
29
ATF_TC_WITHOUT_HEAD(deferred_rc_seen_sets_independent);
30
ATF_TC_WITHOUT_HEAD(deferred_rc_mixed_stop_start);
31
32
ATF_TC_BODY(deferred_rc_init_free, tc)
33
{
34
struct deferred_rc rc;
35
36
pkg_deferred_rc_init(&rc);
37
ATF_REQUIRE_EQ(rc.tmpdir, NULL);
38
ATF_REQUIRE_EQ(rc.to_stop.len, 0);
39
ATF_REQUIRE_EQ(rc.to_start.len, 0);
40
ATF_REQUIRE_EQ(rc.seen_stop, NULL);
41
ATF_REQUIRE_EQ(rc.seen_start, NULL);
42
43
pkg_deferred_rc_free(&rc);
44
ATF_REQUIRE_EQ(rc.tmpdir, NULL);
45
ATF_REQUIRE_EQ(rc.to_stop.len, 0);
46
ATF_REQUIRE_EQ(rc.to_start.len, 0);
47
}
48
49
ATF_TC_BODY(deferred_rc_free_null, tc)
50
{
51
pkg_deferred_rc_free(NULL);
52
}
53
54
ATF_TC_BODY(deferred_rc_stop_entries, tc)
55
{
56
struct deferred_rc rc;
57
struct deferred_rc_stop s;
58
59
pkg_deferred_rc_init(&rc);
60
61
s.name = xstrdup("sshd");
62
s.oldpath = xstrdup("/tmp/fakepath/sshd");
63
vec_push(&rc.to_stop, s);
64
65
s.name = xstrdup("nginx");
66
s.oldpath = NULL;
67
vec_push(&rc.to_stop, s);
68
69
ATF_REQUIRE_EQ(rc.to_stop.len, 2);
70
ATF_REQUIRE_STREQ(rc.to_stop.d[0].name, "sshd");
71
ATF_REQUIRE(rc.to_stop.d[0].oldpath != NULL);
72
ATF_REQUIRE_STREQ(rc.to_stop.d[1].name, "nginx");
73
ATF_REQUIRE_EQ(rc.to_stop.d[1].oldpath, NULL);
74
ATF_REQUIRE_EQ(rc.to_start.len, 0);
75
76
/* Clean up without unlink since paths are fake */
77
free(rc.to_stop.d[0].oldpath);
78
rc.to_stop.d[0].oldpath = NULL;
79
pkg_deferred_rc_free(&rc);
80
}
81
82
ATF_TC_BODY(deferred_rc_start_entries, tc)
83
{
84
struct deferred_rc rc;
85
86
pkg_deferred_rc_init(&rc);
87
88
vec_push(&rc.to_start, xstrdup("postfix"));
89
vec_push(&rc.to_start, xstrdup("dovecot"));
90
91
ATF_REQUIRE_EQ(rc.to_start.len, 2);
92
ATF_REQUIRE_STREQ(rc.to_start.d[0], "postfix");
93
ATF_REQUIRE_STREQ(rc.to_start.d[1], "dovecot");
94
ATF_REQUIRE_EQ(rc.to_stop.len, 0);
95
96
pkg_deferred_rc_free(&rc);
97
}
98
99
ATF_TC_BODY(deferred_rc_dedup, tc)
100
{
101
struct deferred_rc rc;
102
struct deferred_rc_stop s;
103
104
pkg_deferred_rc_init(&rc);
105
106
/* Simulate what pkg_deferred_rc_add does for dedup */
107
pkghash_safe_add(rc.seen_stop, "svc", NULL, NULL);
108
s.name = xstrdup("svc");
109
s.oldpath = NULL;
110
vec_push(&rc.to_stop, s);
111
112
/* Second add should be detected via seen_stop */
113
ATF_REQUIRE(pkghash_get(rc.seen_stop, "svc") != NULL);
114
/* Don't add again — this is what pkg_deferred_rc_add checks */
115
ATF_REQUIRE_EQ(rc.to_stop.len, 1);
116
117
/* Same for start */
118
pkghash_safe_add(rc.seen_start, "svc2", NULL, NULL);
119
vec_push(&rc.to_start, xstrdup("svc2"));
120
121
ATF_REQUIRE(pkghash_get(rc.seen_start, "svc2") != NULL);
122
ATF_REQUIRE_EQ(rc.to_start.len, 1);
123
124
/* Cross-lookup: seen_start used to detect upgrades in execute */
125
pkghash_safe_add(rc.seen_start, "svc", NULL, NULL);
126
ATF_REQUIRE(pkghash_get(rc.seen_start, "svc") != NULL);
127
128
pkg_deferred_rc_free(&rc);
129
}
130
131
ATF_TC_BODY(deferred_rc_tmpdir_cleanup, tc)
132
{
133
struct deferred_rc rc;
134
struct deferred_rc_stop s;
135
char tdir[] = "/tmp/pkg-test-rc.XXXXXX";
136
char script_path[PATH_MAX];
137
char *saved_tmpdir;
138
int fd;
139
140
ATF_REQUIRE(mkdtemp(tdir) != NULL);
141
142
pkg_deferred_rc_init(&rc);
143
rc.tmpdir = xstrdup(tdir);
144
145
snprintf(script_path, sizeof(script_path), "%s/fakesvc", tdir);
146
fd = open(script_path, O_WRONLY | O_CREAT, 0755);
147
ATF_REQUIRE(fd != -1);
148
write(fd, "#!/bin/sh\n", 10);
149
close(fd);
150
151
s.name = xstrdup("fakesvc");
152
s.oldpath = xstrdup(script_path);
153
vec_push(&rc.to_stop, s);
154
155
saved_tmpdir = xstrdup(rc.tmpdir);
156
ATF_REQUIRE(access(script_path, F_OK) == 0);
157
158
pkg_deferred_rc_free(&rc);
159
160
ATF_REQUIRE_EQ_MSG(access(script_path, F_OK), -1,
161
"saved rc script should have been removed");
162
ATF_REQUIRE_EQ_MSG(access(saved_tmpdir, F_OK), -1,
163
"tmpdir should have been removed");
164
165
free(saved_tmpdir);
166
}
167
168
ATF_TC_BODY(deferred_rc_free_reuse, tc)
169
{
170
struct deferred_rc rc;
171
struct deferred_rc_stop s;
172
173
pkg_deferred_rc_init(&rc);
174
175
s.name = xstrdup("sshd");
176
s.oldpath = NULL;
177
vec_push(&rc.to_stop, s);
178
vec_push(&rc.to_start, xstrdup("nginx"));
179
180
pkghash_safe_add(rc.seen_stop, "sshd", NULL, NULL);
181
pkghash_safe_add(rc.seen_start, "nginx", NULL, NULL);
182
183
pkg_deferred_rc_free(&rc);
184
185
/* Re-init and reuse the same struct */
186
pkg_deferred_rc_init(&rc);
187
ATF_REQUIRE_EQ(rc.tmpdir, NULL);
188
ATF_REQUIRE_EQ(rc.to_stop.len, 0);
189
ATF_REQUIRE_EQ(rc.to_start.len, 0);
190
ATF_REQUIRE_EQ(rc.seen_stop, NULL);
191
ATF_REQUIRE_EQ(rc.seen_start, NULL);
192
193
s.name = xstrdup("postfix");
194
s.oldpath = NULL;
195
vec_push(&rc.to_stop, s);
196
ATF_REQUIRE_EQ(rc.to_stop.len, 1);
197
ATF_REQUIRE_STREQ(rc.to_stop.d[0].name, "postfix");
198
199
pkg_deferred_rc_free(&rc);
200
}
201
202
ATF_TC_BODY(deferred_rc_stop_all_null_oldpath, tc)
203
{
204
struct deferred_rc rc;
205
struct deferred_rc_stop s;
206
207
pkg_deferred_rc_init(&rc);
208
209
s.name = xstrdup("sshd");
210
s.oldpath = NULL;
211
vec_push(&rc.to_stop, s);
212
213
s.name = xstrdup("nginx");
214
s.oldpath = NULL;
215
vec_push(&rc.to_stop, s);
216
217
s.name = xstrdup("postfix");
218
s.oldpath = NULL;
219
vec_push(&rc.to_stop, s);
220
221
ATF_REQUIRE_EQ(rc.to_stop.len, 3);
222
ATF_REQUIRE_EQ(rc.to_stop.d[0].oldpath, NULL);
223
ATF_REQUIRE_EQ(rc.to_stop.d[1].oldpath, NULL);
224
ATF_REQUIRE_EQ(rc.to_stop.d[2].oldpath, NULL);
225
226
/* free should handle all-NULL oldpaths without issue */
227
pkg_deferred_rc_free(&rc);
228
}
229
230
ATF_TC_BODY(deferred_rc_tmpdir_multiple_scripts, tc)
231
{
232
struct deferred_rc rc;
233
struct deferred_rc_stop s;
234
char tdir[] = "/tmp/pkg-test-rc.XXXXXX";
235
char path1[PATH_MAX], path2[PATH_MAX], path3[PATH_MAX];
236
int fd;
237
238
ATF_REQUIRE(mkdtemp(tdir) != NULL);
239
240
pkg_deferred_rc_init(&rc);
241
rc.tmpdir = xstrdup(tdir);
242
243
/* Create three fake scripts in the tmpdir */
244
snprintf(path1, sizeof(path1), "%s/svc_a", tdir);
245
fd = open(path1, O_WRONLY | O_CREAT, 0755);
246
ATF_REQUIRE(fd != -1);
247
write(fd, "#!/bin/sh\n", 10);
248
close(fd);
249
250
snprintf(path2, sizeof(path2), "%s/svc_b", tdir);
251
fd = open(path2, O_WRONLY | O_CREAT, 0755);
252
ATF_REQUIRE(fd != -1);
253
write(fd, "#!/bin/sh\n", 10);
254
close(fd);
255
256
snprintf(path3, sizeof(path3), "%s/svc_c", tdir);
257
fd = open(path3, O_WRONLY | O_CREAT, 0755);
258
ATF_REQUIRE(fd != -1);
259
write(fd, "#!/bin/sh\n", 10);
260
close(fd);
261
262
s.name = xstrdup("svc_a");
263
s.oldpath = xstrdup(path1);
264
vec_push(&rc.to_stop, s);
265
266
s.name = xstrdup("svc_b");
267
s.oldpath = xstrdup(path2);
268
vec_push(&rc.to_stop, s);
269
270
s.name = xstrdup("svc_c");
271
s.oldpath = xstrdup(path3);
272
vec_push(&rc.to_stop, s);
273
274
ATF_REQUIRE_EQ(rc.to_stop.len, 3);
275
276
char *saved_tmpdir = xstrdup(rc.tmpdir);
277
pkg_deferred_rc_free(&rc);
278
279
/* All three scripts and the tmpdir should be gone */
280
ATF_REQUIRE_EQ_MSG(access(path1, F_OK), -1,
281
"svc_a script should have been removed");
282
ATF_REQUIRE_EQ_MSG(access(path2, F_OK), -1,
283
"svc_b script should have been removed");
284
ATF_REQUIRE_EQ_MSG(access(path3, F_OK), -1,
285
"svc_c script should have been removed");
286
ATF_REQUIRE_EQ_MSG(access(saved_tmpdir, F_OK), -1,
287
"tmpdir should have been removed");
288
289
free(saved_tmpdir);
290
}
291
292
ATF_TC_BODY(deferred_rc_seen_sets_independent, tc)
293
{
294
struct deferred_rc rc;
295
296
pkg_deferred_rc_init(&rc);
297
298
/* Add to seen_stop only */
299
pkghash_safe_add(rc.seen_stop, "only_stop", NULL, NULL);
300
/* Add to seen_start only */
301
pkghash_safe_add(rc.seen_start, "only_start", NULL, NULL);
302
303
/* Each should be in its own set but not the other */
304
ATF_REQUIRE(pkghash_get(rc.seen_stop, "only_stop") != NULL);
305
ATF_REQUIRE(pkghash_get(rc.seen_start, "only_stop") == NULL);
306
307
ATF_REQUIRE(pkghash_get(rc.seen_start, "only_start") != NULL);
308
ATF_REQUIRE(pkghash_get(rc.seen_stop, "only_start") == NULL);
309
310
/* A name not in either set */
311
ATF_REQUIRE(pkghash_get(rc.seen_stop, "unknown") == NULL);
312
ATF_REQUIRE(pkghash_get(rc.seen_start, "unknown") == NULL);
313
314
pkg_deferred_rc_free(&rc);
315
}
316
317
ATF_TC_BODY(deferred_rc_mixed_stop_start, tc)
318
{
319
struct deferred_rc rc;
320
struct deferred_rc_stop s;
321
322
pkg_deferred_rc_init(&rc);
323
324
/*
325
* Simulate an upgrade scenario: sshd appears in both stop and start.
326
* nginx is only stopped (deleted).
327
* postfix is only started (new install).
328
*/
329
pkghash_safe_add(rc.seen_stop, "sshd", NULL, NULL);
330
s.name = xstrdup("sshd");
331
s.oldpath = NULL;
332
vec_push(&rc.to_stop, s);
333
334
pkghash_safe_add(rc.seen_stop, "nginx", NULL, NULL);
335
s.name = xstrdup("nginx");
336
s.oldpath = NULL;
337
vec_push(&rc.to_stop, s);
338
339
pkghash_safe_add(rc.seen_start, "sshd", NULL, NULL);
340
vec_push(&rc.to_start, xstrdup("sshd"));
341
342
pkghash_safe_add(rc.seen_start, "postfix", NULL, NULL);
343
vec_push(&rc.to_start, xstrdup("postfix"));
344
345
ATF_REQUIRE_EQ(rc.to_stop.len, 2);
346
ATF_REQUIRE_EQ(rc.to_start.len, 2);
347
348
/* sshd is an upgrade: in both sets */
349
ATF_REQUIRE(pkghash_get(rc.seen_stop, "sshd") != NULL);
350
ATF_REQUIRE(pkghash_get(rc.seen_start, "sshd") != NULL);
351
352
/* nginx is a deletion: stop only */
353
ATF_REQUIRE(pkghash_get(rc.seen_stop, "nginx") != NULL);
354
ATF_REQUIRE(pkghash_get(rc.seen_start, "nginx") == NULL);
355
356
/* postfix is a new install: start only */
357
ATF_REQUIRE(pkghash_get(rc.seen_stop, "postfix") == NULL);
358
ATF_REQUIRE(pkghash_get(rc.seen_start, "postfix") != NULL);
359
360
pkg_deferred_rc_free(&rc);
361
}
362
363
ATF_TP_ADD_TCS(tp)
364
{
365
ATF_TP_ADD_TC(tp, deferred_rc_init_free);
366
ATF_TP_ADD_TC(tp, deferred_rc_free_null);
367
ATF_TP_ADD_TC(tp, deferred_rc_stop_entries);
368
ATF_TP_ADD_TC(tp, deferred_rc_start_entries);
369
ATF_TP_ADD_TC(tp, deferred_rc_dedup);
370
ATF_TP_ADD_TC(tp, deferred_rc_tmpdir_cleanup);
371
ATF_TP_ADD_TC(tp, deferred_rc_free_reuse);
372
ATF_TP_ADD_TC(tp, deferred_rc_stop_all_null_oldpath);
373
ATF_TP_ADD_TC(tp, deferred_rc_tmpdir_multiple_scripts);
374
ATF_TP_ADD_TC(tp, deferred_rc_seen_sets_independent);
375
ATF_TP_ADD_TC(tp, deferred_rc_mixed_stop_start);
376
377
return (atf_no_error());
378
}
379
380