Blame


1 9ddaf4ab 2023-04-08 benni /*
2 9ddaf4ab 2023-04-08 benni * Copyright (c) 2023 Benjamin Stürz <benni@stuerz.xyz>
3 9ddaf4ab 2023-04-08 benni *
4 9ddaf4ab 2023-04-08 benni * Permission to use, copy, modify, and distribute this software for any
5 9ddaf4ab 2023-04-08 benni * purpose with or without fee is hereby granted, provided that the above
6 9ddaf4ab 2023-04-08 benni * copyright notice and this permission notice appear in all copies.
7 9ddaf4ab 2023-04-08 benni *
8 9ddaf4ab 2023-04-08 benni * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 9ddaf4ab 2023-04-08 benni * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 9ddaf4ab 2023-04-08 benni * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 9ddaf4ab 2023-04-08 benni * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 9ddaf4ab 2023-04-08 benni * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 9ddaf4ab 2023-04-08 benni * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 9ddaf4ab 2023-04-08 benni * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 9ddaf4ab 2023-04-08 benni */
16 33bf1ca4 2023-04-08 benni #define _XOPEN_SOURCE 700
17 33bf1ca4 2023-04-08 benni #define _BSD_SOURCE 1
18 33bf1ca4 2023-04-08 benni #define DKTYPENAMES
19 7c38b724 2023-05-15 benni #define WSDEBUG 0
20 33bf1ca4 2023-04-08 benni #include <stddef.h>
21 b0a1b420 2023-05-15 benni #include <dev/biovar.h>
22 9c5d8ecc 2023-05-02 benni #include <sys/cdefs.h>
23 33bf1ca4 2023-04-08 benni #include <sys/types.h>
24 33bf1ca4 2023-04-08 benni #include <sys/disklabel.h>
25 33bf1ca4 2023-04-08 benni #include <sys/sysctl.h>
26 33bf1ca4 2023-04-08 benni #include <sys/ioctl.h>
27 33bf1ca4 2023-04-08 benni #include <sys/mount.h>
28 33bf1ca4 2023-04-08 benni #include <sys/dkio.h>
29 33bf1ca4 2023-04-08 benni #include <inttypes.h>
30 8380cc51 2023-05-01 benni #include <stdbool.h>
31 33bf1ca4 2023-04-08 benni #include <string.h>
32 33bf1ca4 2023-04-08 benni #include <stdarg.h>
33 33bf1ca4 2023-04-08 benni #include <stdint.h>
34 33bf1ca4 2023-04-08 benni #include <stdlib.h>
35 33bf1ca4 2023-04-08 benni #include <unistd.h>
36 5691dc7b 2023-05-05 benni #include <libgen.h>
37 33bf1ca4 2023-04-08 benni #include <stdio.h>
38 33bf1ca4 2023-04-08 benni #include <fcntl.h>
39 33bf1ca4 2023-04-08 benni #include <errno.h>
40 0c422a5b 2023-05-05 benni #include <util.h>
41 44121ddf 2023-05-05 benni #include <err.h>
42 33bf1ca4 2023-04-08 benni
43 33bf1ca4 2023-04-08 benni static int diskcount (void)
44 33bf1ca4 2023-04-08 benni {
45 33bf1ca4 2023-04-08 benni const int mib[2] = { CTL_HW, HW_DISKCOUNT };
46 33bf1ca4 2023-04-08 benni int diskcount;
47 33bf1ca4 2023-04-08 benni size_t len = sizeof diskcount;
48 33bf1ca4 2023-04-08 benni
49 33bf1ca4 2023-04-08 benni if (sysctl (mib, 2, &diskcount, &len, NULL, 0) == -1)
50 44121ddf 2023-05-05 benni err (1, "sysctl(hw.diskcount)");
51 33bf1ca4 2023-04-08 benni
52 33bf1ca4 2023-04-08 benni return diskcount;
53 33bf1ca4 2023-04-08 benni }
54 33bf1ca4 2023-04-08 benni
55 33bf1ca4 2023-04-08 benni static char *disknames (void)
56 33bf1ca4 2023-04-08 benni {
57 33bf1ca4 2023-04-08 benni const int num = diskcount ();
58 33bf1ca4 2023-04-08 benni const int mib[2] = { CTL_HW, HW_DISKNAMES };
59 33bf1ca4 2023-04-08 benni size_t len = 32 * num;
60 33bf1ca4 2023-04-08 benni char *buffer = malloc (len);
61 33bf1ca4 2023-04-08 benni
62 33bf1ca4 2023-04-08 benni if (sysctl (mib, 2, buffer, &len, NULL, 0) == -1)
63 44121ddf 2023-05-05 benni err (1, "sysctl(hw.disknames)");
64 33bf1ca4 2023-04-08 benni
65 33bf1ca4 2023-04-08 benni return buffer;
66 33bf1ca4 2023-04-08 benni }
67 33bf1ca4 2023-04-08 benni
68 9c5d8ecc 2023-05-02 benni static char *stripdisk (char *n)
69 33bf1ca4 2023-04-08 benni {
70 9c5d8ecc 2023-05-02 benni const char sufx[] = " disk";
71 9c5d8ecc 2023-05-02 benni const size_t ln = strnlen (n, 16);
72 9c5d8ecc 2023-05-02 benni const size_t ls = sizeof sufx - 1;
73 9c5d8ecc 2023-05-02 benni
74 9c5d8ecc 2023-05-02 benni if (!memcmp (n + ln - ls, sufx, ls)) {
75 9c5d8ecc 2023-05-02 benni n[ln - ls] = '\0';
76 9c5d8ecc 2023-05-02 benni }
77 9c5d8ecc 2023-05-02 benni
78 9c5d8ecc 2023-05-02 benni return n;
79 9c5d8ecc 2023-05-02 benni }
80 9c5d8ecc 2023-05-02 benni
81 9c5d8ecc 2023-05-02 benni static void print_size (uint64_t sz)
82 9c5d8ecc 2023-05-02 benni {
83 33bf1ca4 2023-04-08 benni struct unit {
84 33bf1ca4 2023-04-08 benni char sym;
85 33bf1ca4 2023-04-08 benni uint64_t factor;
86 33bf1ca4 2023-04-08 benni };
87 33bf1ca4 2023-04-08 benni
88 33bf1ca4 2023-04-08 benni const struct unit units[] = {
89 33bf1ca4 2023-04-08 benni { 'P', 1ull << 50 },
90 33bf1ca4 2023-04-08 benni { 'T', 1ull << 40 },
91 33bf1ca4 2023-04-08 benni { 'G', 1ull << 30 },
92 33bf1ca4 2023-04-08 benni { 'M', 1ull << 20 },
93 33bf1ca4 2023-04-08 benni { 'K', 1ull << 10 },
94 33bf1ca4 2023-04-08 benni { '0', 0 },
95 33bf1ca4 2023-04-08 benni };
96 33bf1ca4 2023-04-08 benni
97 33bf1ca4 2023-04-08 benni char sym = 'B';
98 33bf1ca4 2023-04-08 benni uint64_t factor = 1;
99 33bf1ca4 2023-04-08 benni
100 33bf1ca4 2023-04-08 benni for (const struct unit *u = &units[0]; u->factor; ++u) {
101 33bf1ca4 2023-04-08 benni if (sz >= (u->factor * 9 / 10)) {
102 33bf1ca4 2023-04-08 benni sym = u->sym;
103 33bf1ca4 2023-04-08 benni factor = u->factor;
104 33bf1ca4 2023-04-08 benni break;
105 33bf1ca4 2023-04-08 benni }
106 33bf1ca4 2023-04-08 benni }
107 33bf1ca4 2023-04-08 benni
108 33bf1ca4 2023-04-08 benni const unsigned scaled10 = sz * 10 / factor;
109 33bf1ca4 2023-04-08 benni const unsigned scaled = sz / factor;
110 33bf1ca4 2023-04-08 benni if (scaled10 >= 1000) {
111 8a374bef 2023-04-08 benni printf ("%u", scaled);
112 33bf1ca4 2023-04-08 benni } else if (scaled10 >= 100) {
113 8a374bef 2023-04-08 benni printf (" %u", scaled);
114 33bf1ca4 2023-04-08 benni } else {
115 8a374bef 2023-04-08 benni printf ("%u.%u", scaled, scaled10 % 10);
116 33bf1ca4 2023-04-08 benni }
117 33bf1ca4 2023-04-08 benni
118 33bf1ca4 2023-04-08 benni putchar (sym);
119 9c5d8ecc 2023-05-02 benni putchar (' ');
120 33bf1ca4 2023-04-08 benni }
121 33bf1ca4 2023-04-08 benni
122 9c5d8ecc 2023-05-02 benni enum {
123 0c961f7d 2023-05-04 benni FIELD_NAME = 0x01,
124 1115ad3a 2023-05-05 benni FIELD_DUID = 0x02,
125 1115ad3a 2023-05-05 benni FIELD_SIZE = 0x04,
126 1115ad3a 2023-05-05 benni FIELD_USED = 0x08,
127 1115ad3a 2023-05-05 benni FIELD_FREE = 0x10,
128 1115ad3a 2023-05-05 benni FIELD_TYPE = 0x20,
129 1678d261 2023-05-15 benni FIELD_COMMENT = 0x40,
130 9c5d8ecc 2023-05-02 benni
131 1678d261 2023-05-15 benni FIELD_DEFAULT = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_COMMENT,
132 9c5d8ecc 2023-05-02 benni };
133 9c5d8ecc 2023-05-02 benni
134 0c961f7d 2023-05-04 benni enum {
135 0c961f7d 2023-05-04 benni OPT_NOHEADER = 0x01,
136 0c961f7d 2023-05-04 benni OPT_NOUNICODE = 0x02,
137 1678d261 2023-05-15 benni OPT_NOBIO = 0x04,
138 0c961f7d 2023-05-04 benni };
139 0c961f7d 2023-05-04 benni
140 b0a1b420 2023-05-15 benni struct my_diskinfo;
141 b0a1b420 2023-05-15 benni
142 9c5d8ecc 2023-05-02 benni struct my_partinfo {
143 1115ad3a 2023-05-05 benni char letter;
144 9c5d8ecc 2023-05-02 benni uint64_t size;
145 9c5d8ecc 2023-05-02 benni uint64_t fssize;
146 9c5d8ecc 2023-05-02 benni uint64_t free;
147 9c5d8ecc 2023-05-02 benni const char *fstype;
148 9c5d8ecc 2023-05-02 benni const char *mount;
149 1678d261 2023-05-15 benni const struct my_diskinfo *sub; // If this is part of a RAID
150 1678d261 2023-05-15 benni const char *raidstatus; // Only available if (sub != NULL)
151 9c5d8ecc 2023-05-02 benni };
152 9c5d8ecc 2023-05-02 benni
153 9c5d8ecc 2023-05-02 benni struct my_diskinfo {
154 9c5d8ecc 2023-05-02 benni char type[16];
155 9c5d8ecc 2023-05-02 benni char label[16];
156 9c5d8ecc 2023-05-02 benni char name[4];
157 9c5d8ecc 2023-05-02 benni uint64_t size;
158 9c5d8ecc 2023-05-02 benni uint64_t used;
159 1115ad3a 2023-05-05 benni u_char duid[8];
160 b0a1b420 2023-05-15 benni uint8_t num_parts;
161 9c5d8ecc 2023-05-02 benni struct my_partinfo parts[MAXPARTITIONS];
162 1678d261 2023-05-15 benni const char *raidstatus; // If this is a RAID device
163 9c5d8ecc 2023-05-02 benni };
164 9c5d8ecc 2023-05-02 benni
165 7c38b724 2023-05-15 benni struct padding {
166 7c38b724 2023-05-15 benni int name;
167 7c38b724 2023-05-15 benni /* duid = 8 */
168 7c38b724 2023-05-15 benni /* size = 4 */
169 7c38b724 2023-05-15 benni /* used = 4 */
170 7c38b724 2023-05-15 benni /* free = 4 */
171 7c38b724 2023-05-15 benni int type;
172 7c38b724 2023-05-15 benni int comment;
173 7c38b724 2023-05-15 benni };
174 7c38b724 2023-05-15 benni
175 7c38b724 2023-05-15 benni static void print_header (int fields, const struct padding *p)
176 33bf1ca4 2023-04-08 benni {
177 9c5d8ecc 2023-05-02 benni if (fields & FIELD_NAME)
178 7c38b724 2023-05-15 benni printf ("%-*s ", p->name, "NAME");
179 9c5d8ecc 2023-05-02 benni
180 1115ad3a 2023-05-05 benni if (fields & FIELD_DUID)
181 1115ad3a 2023-05-05 benni printf ("%-18s ", "DUID");
182 1115ad3a 2023-05-05 benni
183 9c5d8ecc 2023-05-02 benni if (fields & FIELD_SIZE)
184 9c5d8ecc 2023-05-02 benni printf ("%-4s ", "SIZE");
185 9c5d8ecc 2023-05-02 benni
186 9c5d8ecc 2023-05-02 benni if (fields & FIELD_USED)
187 9c5d8ecc 2023-05-02 benni printf ("%-4s ", "USED");
188 9c5d8ecc 2023-05-02 benni
189 9c5d8ecc 2023-05-02 benni if (fields & FIELD_FREE)
190 9c5d8ecc 2023-05-02 benni printf ("%-4s ", "FREE");
191 9c5d8ecc 2023-05-02 benni
192 9c5d8ecc 2023-05-02 benni if (fields & FIELD_TYPE)
193 7c38b724 2023-05-15 benni printf ("%-*s ", p->type, "TYPE");
194 9c5d8ecc 2023-05-02 benni
195 1678d261 2023-05-15 benni if (fields & FIELD_COMMENT)
196 7c38b724 2023-05-15 benni printf ("%-*s ", p->comment, "COMMENT");
197 9c5d8ecc 2023-05-02 benni
198 7c38b724 2023-05-15 benni #if WSDEBUG
199 7c38b724 2023-05-15 benni putchar ('X');
200 7c38b724 2023-05-15 benni #endif
201 7c38b724 2023-05-15 benni
202 9c5d8ecc 2023-05-02 benni putchar ('\n');
203 33bf1ca4 2023-04-08 benni }
204 33bf1ca4 2023-04-08 benni
205 1115ad3a 2023-05-05 benni static void print_duid (const u_char *duid)
206 3103dec4 2023-05-01 benni {
207 1115ad3a 2023-05-05 benni for (size_t i = 0; i < 8; ++i) {
208 1115ad3a 2023-05-05 benni printf ("%02x", duid[i]);
209 1115ad3a 2023-05-05 benni }
210 1115ad3a 2023-05-05 benni }
211 1115ad3a 2023-05-05 benni
212 7c38b724 2023-05-15 benni static void print_disk (const struct my_diskinfo *, int, int, const char *, const struct padding *);
213 1115ad3a 2023-05-05 benni static void print_part (
214 1115ad3a 2023-05-05 benni const struct my_diskinfo *disk,
215 1115ad3a 2023-05-05 benni const struct my_partinfo *part,
216 1115ad3a 2023-05-05 benni int fields,
217 1115ad3a 2023-05-05 benni int options,
218 7c38b724 2023-05-15 benni bool last,
219 7c38b724 2023-05-15 benni const struct padding *p
220 1115ad3a 2023-05-05 benni ) {
221 0c961f7d 2023-05-04 benni if (fields & FIELD_NAME) {
222 0c961f7d 2023-05-04 benni const char *prefix = (options & OPT_NOUNICODE) ? " " : (last ? "└─" : "├─");
223 7c38b724 2023-05-15 benni printf (
224 7c38b724 2023-05-15 benni "%s%s%c%-*s ",
225 7c38b724 2023-05-15 benni prefix,
226 7c38b724 2023-05-15 benni disk->name,
227 7c38b724 2023-05-15 benni part->letter,
228 7c38b724 2023-05-15 benni p->name - 2 - (int)strlen (disk->name) - 1,
229 7c38b724 2023-05-15 benni ""
230 7c38b724 2023-05-15 benni );
231 0c961f7d 2023-05-04 benni }
232 3103dec4 2023-05-01 benni
233 1115ad3a 2023-05-05 benni if (fields & FIELD_DUID) {
234 1115ad3a 2023-05-05 benni print_duid (disk->duid);
235 1115ad3a 2023-05-05 benni printf (".%c ", part->letter);
236 1115ad3a 2023-05-05 benni }
237 1115ad3a 2023-05-05 benni
238 9c5d8ecc 2023-05-02 benni if (fields & FIELD_SIZE)
239 9c5d8ecc 2023-05-02 benni print_size (part->size);
240 9c5d8ecc 2023-05-02 benni
241 9c5d8ecc 2023-05-02 benni if (fields & FIELD_USED) {
242 9c5d8ecc 2023-05-02 benni if (part->fssize) {
243 9c5d8ecc 2023-05-02 benni print_size (part->fssize - part->free);
244 9c5d8ecc 2023-05-02 benni } else {
245 9c5d8ecc 2023-05-02 benni printf (" N/A ");
246 9c5d8ecc 2023-05-02 benni }
247 3103dec4 2023-05-01 benni }
248 3103dec4 2023-05-01 benni
249 9c5d8ecc 2023-05-02 benni if (fields & FIELD_FREE) {
250 9c5d8ecc 2023-05-02 benni if (part->fssize) {
251 9c5d8ecc 2023-05-02 benni print_size (part->free);
252 9c5d8ecc 2023-05-02 benni } else {
253 9c5d8ecc 2023-05-02 benni printf (" N/A ");
254 9c5d8ecc 2023-05-02 benni }
255 9c5d8ecc 2023-05-02 benni }
256 9c5d8ecc 2023-05-02 benni
257 9c5d8ecc 2023-05-02 benni if (fields & FIELD_TYPE)
258 7c38b724 2023-05-15 benni printf ("%-*s ", p->type, part->fstype);
259 9c5d8ecc 2023-05-02 benni
260 7c38b724 2023-05-15 benni if (fields & FIELD_COMMENT)
261 7c38b724 2023-05-15 benni printf ("%-*s ", p->comment, part->mount ? part->mount : "");
262 9c5d8ecc 2023-05-02 benni
263 7c38b724 2023-05-15 benni #if WSDEBUG
264 7c38b724 2023-05-15 benni putchar ('X');
265 7c38b724 2023-05-15 benni #endif
266 7c38b724 2023-05-15 benni
267 9c5d8ecc 2023-05-02 benni putchar ('\n');
268 b0a1b420 2023-05-15 benni
269 b0a1b420 2023-05-15 benni if (part->sub) {
270 7c38b724 2023-05-15 benni print_disk (part->sub, fields, options, part->raidstatus, p);
271 b0a1b420 2023-05-15 benni }
272 9c5d8ecc 2023-05-02 benni }
273 9c5d8ecc 2023-05-02 benni
274 7c38b724 2023-05-15 benni static void print_disk (
275 7c38b724 2023-05-15 benni const struct my_diskinfo *disk,
276 7c38b724 2023-05-15 benni int fields,
277 7c38b724 2023-05-15 benni int options,
278 7c38b724 2023-05-15 benni const char *raidstatus,
279 7c38b724 2023-05-15 benni const struct padding *p
280 7c38b724 2023-05-15 benni ) {
281 b0a1b420 2023-05-15 benni if (fields & FIELD_NAME) {
282 7c38b724 2023-05-15 benni const char *prefix = raidstatus ? "│ └─" : "";
283 7c38b724 2023-05-15 benni
284 7c38b724 2023-05-15 benni printf (
285 7c38b724 2023-05-15 benni "%s%-*s ",
286 7c38b724 2023-05-15 benni prefix,
287 7c38b724 2023-05-15 benni p->name - (int)strlen (prefix),
288 7c38b724 2023-05-15 benni disk->name
289 7c38b724 2023-05-15 benni );
290 b0a1b420 2023-05-15 benni }
291 9c5d8ecc 2023-05-02 benni
292 1115ad3a 2023-05-05 benni if (fields & FIELD_DUID) {
293 1115ad3a 2023-05-05 benni print_duid (disk->duid);
294 1115ad3a 2023-05-05 benni printf (" ");
295 1115ad3a 2023-05-05 benni }
296 1115ad3a 2023-05-05 benni
297 9c5d8ecc 2023-05-02 benni if (fields & FIELD_SIZE)
298 9c5d8ecc 2023-05-02 benni print_size (disk->size);
299 9c5d8ecc 2023-05-02 benni
300 9c5d8ecc 2023-05-02 benni if (fields & FIELD_USED)
301 9c5d8ecc 2023-05-02 benni print_size (disk->used);
302 9c5d8ecc 2023-05-02 benni
303 9c5d8ecc 2023-05-02 benni if (fields & FIELD_FREE)
304 9c5d8ecc 2023-05-02 benni print_size (disk->size - disk->used);
305 9c5d8ecc 2023-05-02 benni
306 9c5d8ecc 2023-05-02 benni if (fields & FIELD_TYPE)
307 7c38b724 2023-05-15 benni printf ("%-*.16s ", p->type, disk->type);
308 9c5d8ecc 2023-05-02 benni
309 1678d261 2023-05-15 benni if (fields & FIELD_COMMENT) {
310 1678d261 2023-05-15 benni if (raidstatus) {
311 7c38b724 2023-05-15 benni printf ("%-*s ", p->comment, raidstatus);
312 1678d261 2023-05-15 benni } else if (disk->raidstatus) {
313 7c38b724 2023-05-15 benni printf (
314 7c38b724 2023-05-15 benni "%.16s (%s)%*s ",
315 7c38b724 2023-05-15 benni disk->label,
316 7c38b724 2023-05-15 benni disk->raidstatus,
317 7c38b724 2023-05-15 benni p->comment - (int)strnlen (disk->label, sizeof disk->label) - (int)strlen (disk->raidstatus) - 3,
318 7c38b724 2023-05-15 benni ""
319 7c38b724 2023-05-15 benni );
320 1678d261 2023-05-15 benni } else {
321 7c38b724 2023-05-15 benni printf ("%-*.16s ", p->comment, disk->label);
322 1678d261 2023-05-15 benni }
323 1678d261 2023-05-15 benni }
324 9c5d8ecc 2023-05-02 benni
325 7c38b724 2023-05-15 benni #if WSDEBUG
326 7c38b724 2023-05-15 benni putchar ('X');
327 7c38b724 2023-05-15 benni #endif
328 7c38b724 2023-05-15 benni
329 9c5d8ecc 2023-05-02 benni putchar ('\n');
330 9c5d8ecc 2023-05-02 benni
331 1678d261 2023-05-15 benni if (!raidstatus) {
332 b0a1b420 2023-05-15 benni for (uint8_t i = 0; i < disk->num_parts; ++i)
333 7c38b724 2023-05-15 benni print_part (disk, &disk->parts[i], fields, options, i == (disk->num_parts - 1), p);
334 b0a1b420 2023-05-15 benni }
335 9c5d8ecc 2023-05-02 benni }
336 9c5d8ecc 2023-05-02 benni
337 9c5d8ecc 2023-05-02 benni static const struct statfs *find_mount (const char *dev)
338 9c5d8ecc 2023-05-02 benni {
339 9c5d8ecc 2023-05-02 benni static struct statfs *mounts = NULL;
340 9c5d8ecc 2023-05-02 benni static int n_mounts;
341 9c5d8ecc 2023-05-02 benni
342 9c5d8ecc 2023-05-02 benni if (!mounts) {
343 9c5d8ecc 2023-05-02 benni n_mounts = getmntinfo (&mounts, MNT_NOWAIT);
344 9c5d8ecc 2023-05-02 benni if (n_mounts == 0)
345 44121ddf 2023-05-05 benni err (1, "getmntinfo()");
346 9c5d8ecc 2023-05-02 benni }
347 9c5d8ecc 2023-05-02 benni
348 9c5d8ecc 2023-05-02 benni for (int i = 0; i < n_mounts; ++i) {
349 9c5d8ecc 2023-05-02 benni if (!strcmp (dev, mounts[i].f_mntfromname))
350 9c5d8ecc 2023-05-02 benni return &mounts[i];
351 9c5d8ecc 2023-05-02 benni }
352 9c5d8ecc 2023-05-02 benni
353 9c5d8ecc 2023-05-02 benni return NULL;
354 3103dec4 2023-05-01 benni }
355 3103dec4 2023-05-01 benni
356 9c5d8ecc 2023-05-02 benni static struct my_diskinfo read_disk (const char *name)
357 9c5d8ecc 2023-05-02 benni {
358 9c5d8ecc 2023-05-02 benni struct my_diskinfo disk;
359 9c5d8ecc 2023-05-02 benni struct disklabel label;
360 0c422a5b 2023-05-05 benni char *ppath, *letter;
361 9c5d8ecc 2023-05-02 benni
362 9c5d8ecc 2023-05-02 benni bzero (&disk, sizeof disk);
363 9c5d8ecc 2023-05-02 benni
364 9c5d8ecc 2023-05-02 benni { // Read disklabel.
365 0c422a5b 2023-05-05 benni size_t len;
366 9c5d8ecc 2023-05-02 benni int fd;
367 9c5d8ecc 2023-05-02 benni
368 0c422a5b 2023-05-05 benni fd = opendev (name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, &ppath);
369 9c5d8ecc 2023-05-02 benni if (fd < 0)
370 44121ddf 2023-05-05 benni err (1, "opendev(%s)", name);
371 0c422a5b 2023-05-05 benni
372 9c5d8ecc 2023-05-02 benni if (ioctl (fd, DIOCGDINFO, &label) < 0)
373 44121ddf 2023-05-05 benni err (1, "ioctl(%s, DIOCGDINFO)", name);
374 9c5d8ecc 2023-05-02 benni close (fd);
375 9c5d8ecc 2023-05-02 benni
376 0c422a5b 2023-05-05 benni len = strlen (ppath);
377 0c422a5b 2023-05-05 benni letter = ppath + len - 1;
378 0c422a5b 2023-05-05 benni }
379 5691dc7b 2023-05-05 benni
380 9c5d8ecc 2023-05-02 benni memcpy (disk.name, name, 3);
381 9c5d8ecc 2023-05-02 benni disk.name[3] = '\0';
382 9c5d8ecc 2023-05-02 benni disk.size = DL_GETDSIZE (&label) * label.d_secsize;
383 9c5d8ecc 2023-05-02 benni memcpy (disk.type, label.d_typename, 16);
384 9c5d8ecc 2023-05-02 benni stripdisk (disk.type);
385 9c5d8ecc 2023-05-02 benni memcpy (disk.label, label.d_packname, 16);
386 1115ad3a 2023-05-05 benni memcpy (disk.duid, label.d_uid, sizeof disk.duid);
387 9c5d8ecc 2023-05-02 benni disk.num_parts = 0;
388 9c5d8ecc 2023-05-02 benni
389 9c5d8ecc 2023-05-02 benni for (uint16_t i = 0; i < label.d_npartitions; ++i) {
390 9c5d8ecc 2023-05-02 benni const struct partition *p = &label.d_partitions[i];
391 9c5d8ecc 2023-05-02 benni const struct statfs *mnt;
392 9c5d8ecc 2023-05-02 benni struct my_partinfo part;
393 9c5d8ecc 2023-05-02 benni
394 9c5d8ecc 2023-05-02 benni bzero (&part, sizeof part);
395 9c5d8ecc 2023-05-02 benni
396 9c5d8ecc 2023-05-02 benni part.size = DL_GETPSIZE (p) * label.d_secsize;
397 9c5d8ecc 2023-05-02 benni
398 9c5d8ecc 2023-05-02 benni if (!part.size)
399 9c5d8ecc 2023-05-02 benni continue;
400 9c5d8ecc 2023-05-02 benni
401 1115ad3a 2023-05-05 benni part.letter = 'a' + i;
402 0c422a5b 2023-05-05 benni *letter = part.letter;
403 0c422a5b 2023-05-05 benni mnt = find_mount (ppath);
404 9c5d8ecc 2023-05-02 benni
405 9c5d8ecc 2023-05-02 benni if (mnt) {
406 9c5d8ecc 2023-05-02 benni const uint64_t bs = mnt->f_bsize;
407 9c5d8ecc 2023-05-02 benni part.mount = mnt->f_mntonname;
408 9c5d8ecc 2023-05-02 benni part.fssize = mnt->f_blocks * bs;
409 9c5d8ecc 2023-05-02 benni part.free = mnt->f_bfree * bs;
410 9c5d8ecc 2023-05-02 benni }
411 9c5d8ecc 2023-05-02 benni
412 9c5d8ecc 2023-05-02 benni part.fstype = fstypenames[p->p_fstype];
413 9c5d8ecc 2023-05-02 benni if (i != 2)
414 9c5d8ecc 2023-05-02 benni disk.used += part.size;
415 9c5d8ecc 2023-05-02 benni disk.parts[disk.num_parts++] = part;
416 9c5d8ecc 2023-05-02 benni }
417 9c5d8ecc 2023-05-02 benni
418 9c5d8ecc 2023-05-02 benni return disk;
419 9c5d8ecc 2023-05-02 benni }
420 9c5d8ecc 2023-05-02 benni
421 1678d261 2023-05-15 benni static const char *bd_statusstr (int status) {
422 1678d261 2023-05-15 benni switch (status) {
423 1678d261 2023-05-15 benni case BIOC_SDONLINE: return BIOC_SDONLINE_S;
424 1678d261 2023-05-15 benni case BIOC_SDOFFLINE: return BIOC_SDOFFLINE_S;
425 1678d261 2023-05-15 benni case BIOC_SDFAILED: return BIOC_SDFAILED_S;
426 1678d261 2023-05-15 benni case BIOC_SDREBUILD: return BIOC_SDREBUILD_S;
427 1678d261 2023-05-15 benni case BIOC_SDHOTSPARE: return BIOC_SDHOTSPARE_S;
428 1678d261 2023-05-15 benni case BIOC_SDUNUSED: return BIOC_SDUNUSED_S;
429 1678d261 2023-05-15 benni case BIOC_SDSCRUB: return BIOC_SDSCRUB_S;
430 1678d261 2023-05-15 benni case BIOC_SDINVALID: return BIOC_SDINVALID_S;
431 1678d261 2023-05-15 benni default: return "Unknown";
432 1678d261 2023-05-15 benni }
433 1678d261 2023-05-15 benni }
434 1678d261 2023-05-15 benni
435 1678d261 2023-05-15 benni static const char *bv_statusstr (int status) {
436 1678d261 2023-05-15 benni switch (status) {
437 1678d261 2023-05-15 benni case BIOC_SVONLINE: return BIOC_SVONLINE_S;
438 1678d261 2023-05-15 benni case BIOC_SVOFFLINE: return BIOC_SVOFFLINE_S;
439 1678d261 2023-05-15 benni case BIOC_SVDEGRADED: return BIOC_SVDEGRADED_S;
440 1678d261 2023-05-15 benni case BIOC_SVBUILDING: return BIOC_SVBUILDING_S;
441 1678d261 2023-05-15 benni case BIOC_SVSCRUB: return BIOC_SVSCRUB_S;
442 1678d261 2023-05-15 benni case BIOC_SVREBUILD: return BIOC_SVREBUILD_S;
443 1678d261 2023-05-15 benni case BIOC_SVINVALID: return BIOC_SVINVALID_S;
444 1678d261 2023-05-15 benni default: return "Unknown";
445 1678d261 2023-05-15 benni }
446 1678d261 2023-05-15 benni }
447 1678d261 2023-05-15 benni
448 b0a1b420 2023-05-15 benni static void read_raid (
449 1678d261 2023-05-15 benni struct my_diskinfo *disk,
450 b0a1b420 2023-05-15 benni struct my_diskinfo *disks,
451 b0a1b420 2023-05-15 benni size_t num_disks
452 b0a1b420 2023-05-15 benni ) {
453 b0a1b420 2023-05-15 benni struct bioc_inq bi;
454 b0a1b420 2023-05-15 benni int fd;
455 b0a1b420 2023-05-15 benni
456 b0a1b420 2023-05-15 benni fd = opendev (disk->name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, NULL);
457 b0a1b420 2023-05-15 benni if (fd < 0) {
458 b0a1b420 2023-05-15 benni warn ("read_raid(): opendev(%s)", disk->name);
459 b0a1b420 2023-05-15 benni return;
460 b0a1b420 2023-05-15 benni }
461 b0a1b420 2023-05-15 benni
462 b0a1b420 2023-05-15 benni bzero (&bi, sizeof bi);
463 b0a1b420 2023-05-15 benni
464 b0a1b420 2023-05-15 benni if (ioctl (fd, BIOCINQ, &bi) == -1)
465 b0a1b420 2023-05-15 benni goto ret;
466 b0a1b420 2023-05-15 benni
467 1678d261 2023-05-15 benni for (int i = 0; i < bi.bi_novol; ++i) {
468 1678d261 2023-05-15 benni struct bioc_vol bv;
469 b0a1b420 2023-05-15 benni
470 1678d261 2023-05-15 benni bzero (&bv, sizeof bv);
471 1678d261 2023-05-15 benni memcpy (&bv.bv_bio, &bi.bi_bio, sizeof bi.bi_bio);
472 1678d261 2023-05-15 benni bv.bv_volid = i;
473 1678d261 2023-05-15 benni
474 1678d261 2023-05-15 benni if (ioctl (fd, BIOCVOL, &bv) == -1) {
475 1678d261 2023-05-15 benni warn ("read_raid(%s): BIOCVOL(%d)", disk->name, i);
476 b0a1b420 2023-05-15 benni continue;
477 b0a1b420 2023-05-15 benni }
478 b0a1b420 2023-05-15 benni
479 1678d261 2023-05-15 benni if (strcmp (disk->name, bv.bv_dev) != 0)
480 b0a1b420 2023-05-15 benni continue;
481 b0a1b420 2023-05-15 benni
482 1678d261 2023-05-15 benni disk->raidstatus = bv_statusstr (bv.bv_status);
483 1678d261 2023-05-15 benni
484 1678d261 2023-05-15 benni for (int j = 0; j < bv.bv_nodisk; ++j) {
485 1678d261 2023-05-15 benni struct bioc_disk bd;
486 1678d261 2023-05-15 benni size_t len_vendor;
487 1678d261 2023-05-15 benni char letter;
488 1678d261 2023-05-15 benni
489 1678d261 2023-05-15 benni bzero (&bd, sizeof bd);
490 1678d261 2023-05-15 benni memcpy (&bd.bd_bio, &bi.bi_bio, sizeof bi.bi_bio);
491 1678d261 2023-05-15 benni bd.bd_volid = i;
492 1678d261 2023-05-15 benni bd.bd_diskid = j;
493 1678d261 2023-05-15 benni
494 1678d261 2023-05-15 benni if (ioctl (fd, BIOCDISK, &bd) == -1) {
495 1678d261 2023-05-15 benni warn ("read_raid(%s): BIOCDISK(%d, %d)", disk->name, i, j);
496 1678d261 2023-05-15 benni continue;
497 1678d261 2023-05-15 benni }
498 1678d261 2023-05-15 benni
499 1678d261 2023-05-15 benni len_vendor = strlen (bd.bd_vendor);
500 1678d261 2023-05-15 benni if (len_vendor != 4) {
501 1678d261 2023-05-15 benni warnx ("read_raid(%s): unexpected vendor string: %.32s", disk->name, bd.bd_vendor);
502 1678d261 2023-05-15 benni continue;
503 1678d261 2023-05-15 benni }
504 1678d261 2023-05-15 benni letter = bd.bd_vendor[len_vendor - 1];
505 1678d261 2023-05-15 benni
506 1678d261 2023-05-15 benni for (size_t k = 0; k < num_disks; ++k) {
507 1678d261 2023-05-15 benni if (!memcmp (bd.bd_vendor, disks[k].name, 3)) {
508 1678d261 2023-05-15 benni for (size_t l = 0; l < disks[k].num_parts; ++l) {
509 1678d261 2023-05-15 benni if (letter == disks[k].parts[l].letter) {
510 1678d261 2023-05-15 benni disks[k].parts[l].sub = disk;
511 1678d261 2023-05-15 benni disks[k].parts[l].raidstatus = bd_statusstr(bd.bd_status);
512 1678d261 2023-05-15 benni goto found;
513 1678d261 2023-05-15 benni }
514 b0a1b420 2023-05-15 benni }
515 b0a1b420 2023-05-15 benni }
516 b0a1b420 2023-05-15 benni }
517 1678d261 2023-05-15 benni found:;
518 b0a1b420 2023-05-15 benni }
519 b0a1b420 2023-05-15 benni }
520 b0a1b420 2023-05-15 benni ret:
521 b0a1b420 2023-05-15 benni close (fd);
522 b0a1b420 2023-05-15 benni }
523 b0a1b420 2023-05-15 benni
524 9c5d8ecc 2023-05-02 benni static int usage (void)
525 9c5d8ecc 2023-05-02 benni {
526 1678d261 2023-05-15 benni fputs ("Usage: lsblk [-abinUuV] [disk...]\n", stderr);
527 9c5d8ecc 2023-05-02 benni return 1;
528 4d8e470c 2023-05-15 benni }
529 4d8e470c 2023-05-15 benni
530 7c38b724 2023-05-15 benni static void pad_update (int *pad, int newval)
531 7c38b724 2023-05-15 benni {
532 7c38b724 2023-05-15 benni if (newval > *pad)
533 7c38b724 2023-05-15 benni *pad = newval;
534 7c38b724 2023-05-15 benni }
535 7c38b724 2023-05-15 benni
536 7c38b724 2023-05-15 benni static void pad_disk (struct padding *p, const struct my_diskinfo *disk)
537 7c38b724 2023-05-15 benni {
538 7c38b724 2023-05-15 benni size_t len_disk;
539 7c38b724 2023-05-15 benni int comment;
540 7c38b724 2023-05-15 benni
541 7c38b724 2023-05-15 benni len_disk = strnlen (disk->name, sizeof disk->name);
542 7c38b724 2023-05-15 benni comment = strnlen (disk->label, sizeof disk->label);
543 7c38b724 2023-05-15 benni if (disk->raidstatus)
544 7c38b724 2023-05-15 benni comment += strlen (disk->raidstatus) + 3;
545 7c38b724 2023-05-15 benni
546 7c38b724 2023-05-15 benni pad_update (&p->name, len_disk);
547 7c38b724 2023-05-15 benni pad_update (&p->type, strnlen (disk->type, sizeof disk->type));
548 7c38b724 2023-05-15 benni pad_update (&p->comment, comment);
549 7c38b724 2023-05-15 benni
550 7c38b724 2023-05-15 benni for (int i = 0; i < disk->num_parts; ++i) {
551 7c38b724 2023-05-15 benni const struct my_partinfo *part = &disk->parts[i];
552 7c38b724 2023-05-15 benni
553 7c38b724 2023-05-15 benni pad_update (&p->name, len_disk + 3);
554 7c38b724 2023-05-15 benni pad_update (&p->type, strlen (part->fstype));
555 7c38b724 2023-05-15 benni
556 7c38b724 2023-05-15 benni if (part->sub) {
557 7c38b724 2023-05-15 benni pad_update (&p->name, strnlen (part->sub->name, sizeof part->sub->name) + 4);
558 7c38b724 2023-05-15 benni pad_update (&p->comment, strlen (part->raidstatus));
559 7c38b724 2023-05-15 benni } else if (part->mount) {
560 7c38b724 2023-05-15 benni pad_update (&p->comment, strlen (part->mount));
561 7c38b724 2023-05-15 benni }
562 7c38b724 2023-05-15 benni }
563 7c38b724 2023-05-15 benni }
564 7c38b724 2023-05-15 benni
565 7c38b724 2023-05-15 benni
566 4d8e470c 2023-05-15 benni static int compare_disk (const void *p1, const void *p2)
567 4d8e470c 2023-05-15 benni {
568 4d8e470c 2023-05-15 benni const struct my_diskinfo *d1 = p1;
569 4d8e470c 2023-05-15 benni const struct my_diskinfo *d2 = p2;
570 4d8e470c 2023-05-15 benni
571 4d8e470c 2023-05-15 benni return strcmp (d1->name, d2->name);
572 9c5d8ecc 2023-05-02 benni }
573 9c5d8ecc 2023-05-02 benni
574 33bf1ca4 2023-04-08 benni int main (int argc, char *argv[])
575 33bf1ca4 2023-04-08 benni {
576 8380cc51 2023-05-01 benni int option;
577 9c5d8ecc 2023-05-02 benni int fields = FIELD_DEFAULT;
578 0c961f7d 2023-05-04 benni int options = 0;
579 8380cc51 2023-05-01 benni
580 aff15c23 2023-04-30 benni if (unveil ("/dev", "r") == -1)
581 44121ddf 2023-05-05 benni err (1, "unveil(/dev)");
582 aff15c23 2023-04-30 benni
583 aff15c23 2023-04-30 benni if (unveil (NULL, NULL) == -1)
584 44121ddf 2023-05-05 benni err (1, "unveil()");
585 aff15c23 2023-04-30 benni
586 1678d261 2023-05-15 benni while ((option = getopt (argc, argv, ":abinUuV")) != -1) {
587 33bf1ca4 2023-04-08 benni switch (option) {
588 bf0e6461 2023-05-02 benni case 'a':
589 bf0e6461 2023-05-02 benni fields = -1;
590 8380cc51 2023-05-01 benni break;
591 1678d261 2023-05-15 benni case 'b':
592 1678d261 2023-05-15 benni options |= OPT_NOBIO;
593 1678d261 2023-05-15 benni break;
594 0c961f7d 2023-05-04 benni case 'i':
595 0c961f7d 2023-05-04 benni options |= OPT_NOUNICODE;
596 0c961f7d 2023-05-04 benni break;
597 bf0e6461 2023-05-02 benni case 'n':
598 0c961f7d 2023-05-04 benni options |= OPT_NOHEADER;
599 bf0e6461 2023-05-02 benni break;
600 1115ad3a 2023-05-05 benni case 'U':
601 1115ad3a 2023-05-05 benni fields |= FIELD_DUID;
602 1115ad3a 2023-05-05 benni break;
603 9c5d8ecc 2023-05-02 benni case 'u':
604 9c5d8ecc 2023-05-02 benni fields |= FIELD_USED | FIELD_FREE;
605 9c5d8ecc 2023-05-02 benni break;
606 1115ad3a 2023-05-05 benni case 'V':
607 1115ad3a 2023-05-05 benni puts ("lsblk-" VERSION);
608 1115ad3a 2023-05-05 benni return 0;
609 33bf1ca4 2023-04-08 benni default:
610 33bf1ca4 2023-04-08 benni return usage ();
611 33bf1ca4 2023-04-08 benni }
612 33bf1ca4 2023-04-08 benni }
613 33bf1ca4 2023-04-08 benni
614 5691dc7b 2023-05-05 benni argv += optind;
615 5691dc7b 2023-05-05 benni argc -= optind;
616 33bf1ca4 2023-04-08 benni
617 5691dc7b 2023-05-05 benni char *names = argc == 0 ? disknames () : NULL;
618 33bf1ca4 2023-04-08 benni
619 33bf1ca4 2023-04-08 benni if (pledge ("stdio rpath disklabel", NULL) == -1)
620 44121ddf 2023-05-05 benni err (1, "pledge()");
621 33bf1ca4 2023-04-08 benni
622 5691dc7b 2023-05-05 benni size_t cap_disks;
623 5691dc7b 2023-05-05 benni if (argc == 0) {
624 5691dc7b 2023-05-05 benni const char *s = names;
625 5691dc7b 2023-05-05 benni cap_disks = 0;
626 5691dc7b 2023-05-05 benni while ((s = strchr (s, ',')) != NULL)
627 5691dc7b 2023-05-05 benni ++cap_disks, ++s;
628 5691dc7b 2023-05-05 benni } else {
629 5691dc7b 2023-05-05 benni cap_disks = argc;
630 5691dc7b 2023-05-05 benni }
631 5691dc7b 2023-05-05 benni
632 9c5d8ecc 2023-05-02 benni struct my_diskinfo *disks = calloc (cap_disks, sizeof (struct my_diskinfo));
633 5691dc7b 2023-05-05 benni size_t num_disks = 0;
634 8380cc51 2023-05-01 benni
635 5691dc7b 2023-05-05 benni if (argc == 0) {
636 5691dc7b 2023-05-05 benni for (char *disk; (disk = strsep (&names, ",")) != NULL; ) {
637 5691dc7b 2023-05-05 benni char *colon = strchr (disk, ':');
638 5691dc7b 2023-05-05 benni if (colon)
639 5691dc7b 2023-05-05 benni *colon = '\0';
640 5691dc7b 2023-05-05 benni disks[num_disks++] = read_disk (disk);
641 5691dc7b 2023-05-05 benni if (colon)
642 5691dc7b 2023-05-05 benni *colon = ':';
643 33bf1ca4 2023-04-08 benni }
644 5691dc7b 2023-05-05 benni } else {
645 5691dc7b 2023-05-05 benni for (int i = 0; i < argc; ++i) {
646 5691dc7b 2023-05-05 benni char *disk = basename (argv[i]);
647 5691dc7b 2023-05-05 benni disks[num_disks++] = read_disk (disk);
648 5691dc7b 2023-05-05 benni }
649 33bf1ca4 2023-04-08 benni }
650 33bf1ca4 2023-04-08 benni
651 33bf1ca4 2023-04-08 benni free (names);
652 9c5d8ecc 2023-05-02 benni
653 4d8e470c 2023-05-15 benni mergesort (disks, num_disks, sizeof *disks, compare_disk);
654 4d8e470c 2023-05-15 benni
655 1678d261 2023-05-15 benni if (!(options & OPT_NOBIO)) {
656 1678d261 2023-05-15 benni for (size_t i = 0; i < num_disks; ++i) {
657 1678d261 2023-05-15 benni read_raid (&disks[i], disks, num_disks);
658 1678d261 2023-05-15 benni }
659 b0a1b420 2023-05-15 benni }
660 b0a1b420 2023-05-15 benni
661 7c38b724 2023-05-15 benni struct padding p = {
662 7c38b724 2023-05-15 benni .name = strlen ("NAME"),
663 7c38b724 2023-05-15 benni .type = strlen ("TYPE"),
664 7c38b724 2023-05-15 benni .comment = strlen ("COMMENT"),
665 7c38b724 2023-05-15 benni };
666 7c38b724 2023-05-15 benni
667 7c38b724 2023-05-15 benni for (size_t i = 0; i < num_disks; ++i)
668 7c38b724 2023-05-15 benni pad_disk (&p, &disks[i]);
669 7c38b724 2023-05-15 benni
670 0c961f7d 2023-05-04 benni if (!(options & OPT_NOHEADER))
671 7c38b724 2023-05-15 benni print_header (fields, &p);
672 9c5d8ecc 2023-05-02 benni
673 9c5d8ecc 2023-05-02 benni for (size_t i = 0; i < num_disks; ++i) {
674 7c38b724 2023-05-15 benni print_disk (&disks[i], fields, options, NULL, &p);
675 9c5d8ecc 2023-05-02 benni }
676 5691dc7b 2023-05-05 benni
677 33bf1ca4 2023-04-08 benni return 0;
678 33bf1ca4 2023-04-08 benni }