Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/pkg-serve/pkg-serve.c
178435 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2026 Baptiste Daroussin <[email protected]>
5
*/
6
7
/*
8
* Speaks the same protocol as "pkg ssh" (see pkg-ssh(8)):
9
* -> ok: pkg-serve <version>
10
* <- get <file> <mtime>
11
* -> ok: <size>\n<data> or ok: 0\n or ko: <error>\n
12
* <- quit
13
*/
14
15
#include <sys/capsicum.h>
16
#include <sys/stat.h>
17
18
#include <ctype.h>
19
#include <err.h>
20
#include <errno.h>
21
#include <fcntl.h>
22
#include <inttypes.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <unistd.h>
27
28
#define VERSION "0.1"
29
#define BUFSZ 32768
30
31
static void
32
usage(void)
33
{
34
fprintf(stderr, "usage: pkg-serve basedir\n");
35
exit(EXIT_FAILURE);
36
}
37
38
int
39
main(int argc, char *argv[])
40
{
41
struct stat st;
42
cap_rights_t rights;
43
char *line = NULL;
44
char *file, *age;
45
size_t linecap = 0, r, toread;
46
ssize_t linelen;
47
off_t remaining;
48
time_t mtime;
49
char *end;
50
int fd, ffd;
51
char buf[BUFSZ];
52
const char *basedir;
53
54
if (argc != 2)
55
usage();
56
57
basedir = argv[1];
58
59
if ((fd = open(basedir, O_DIRECTORY | O_RDONLY | O_CLOEXEC)) < 0)
60
err(EXIT_FAILURE, "open(%s)", basedir);
61
62
cap_rights_init(&rights, CAP_READ, CAP_FSTATAT, CAP_LOOKUP,
63
CAP_FCNTL);
64
if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS)
65
err(EXIT_FAILURE, "cap_rights_limit");
66
67
if (cap_enter() < 0 && errno != ENOSYS)
68
err(EXIT_FAILURE, "cap_enter");
69
70
printf("ok: pkg-serve " VERSION "\n");
71
fflush(stdout);
72
73
while ((linelen = getline(&line, &linecap, stdin)) > 0) {
74
/* trim newline */
75
if (linelen > 0 && line[linelen - 1] == '\n')
76
line[--linelen] = '\0';
77
78
if (linelen == 0)
79
continue;
80
81
if (strcmp(line, "quit") == 0)
82
break;
83
84
if (strncmp(line, "get ", 4) != 0) {
85
printf("ko: unknown command '%s'\n", line);
86
fflush(stdout);
87
continue;
88
}
89
90
file = line + 4;
91
92
if (*file == '\0') {
93
printf("ko: bad command get, expecting 'get file age'\n");
94
fflush(stdout);
95
continue;
96
}
97
98
/* skip leading slash */
99
if (*file == '/')
100
file++;
101
102
/* find the age argument */
103
age = file;
104
while (*age != '\0' && !isspace((unsigned char)*age))
105
age++;
106
107
if (*age == '\0') {
108
printf("ko: bad command get, expecting 'get file age'\n");
109
fflush(stdout);
110
continue;
111
}
112
113
*age++ = '\0';
114
115
/* skip whitespace */
116
while (isspace((unsigned char)*age))
117
age++;
118
119
if (*age == '\0') {
120
printf("ko: bad command get, expecting 'get file age'\n");
121
fflush(stdout);
122
continue;
123
}
124
125
errno = 0;
126
mtime = (time_t)strtoimax(age, &end, 10);
127
if (errno != 0 || *end != '\0' || end == age) {
128
printf("ko: bad number %s\n", age);
129
fflush(stdout);
130
continue;
131
}
132
133
if (fstatat(fd, file, &st, AT_RESOLVE_BENEATH) == -1) {
134
printf("ko: file not found\n");
135
fflush(stdout);
136
continue;
137
}
138
139
if (!S_ISREG(st.st_mode)) {
140
printf("ko: not a file\n");
141
fflush(stdout);
142
continue;
143
}
144
145
if (st.st_mtime <= mtime) {
146
printf("ok: 0\n");
147
fflush(stdout);
148
continue;
149
}
150
151
if ((ffd = openat(fd, file, O_RDONLY | O_RESOLVE_BENEATH)) == -1) {
152
printf("ko: file not found\n");
153
fflush(stdout);
154
continue;
155
}
156
157
printf("ok: %" PRIdMAX "\n", (intmax_t)st.st_size);
158
fflush(stdout);
159
160
remaining = st.st_size;
161
while (remaining > 0) {
162
toread = sizeof(buf);
163
if ((off_t)toread > remaining)
164
toread = (size_t)remaining;
165
r = read(ffd, buf, toread);
166
if (r <= 0)
167
break;
168
if (fwrite(buf, 1, r, stdout) != r)
169
break;
170
remaining -= r;
171
}
172
close(ffd);
173
if (remaining > 0)
174
errx(EXIT_FAILURE, "%s: file truncated during transfer",
175
file);
176
fflush(stdout);
177
}
178
179
return (EXIT_SUCCESS);
180
}
181
182