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 185836d0 2023-05-15 benni #include <ctype.h>
40 33bf1ca4 2023-04-08 benni #include <errno.h>
41 0c422a5b 2023-05-05 benni #include <util.h>
42 44121ddf 2023-05-05 benni #include <err.h>
43 33bf1ca4 2023-04-08 benni
44 33bf1ca4 2023-04-08 benni static int diskcount (void)
45 33bf1ca4 2023-04-08 benni {
46 33bf1ca4 2023-04-08 benni const int mib[2] = { CTL_HW, HW_DISKCOUNT };
47 33bf1ca4 2023-04-08 benni int diskcount;
48 33bf1ca4 2023-04-08 benni size_t len = sizeof diskcount;
49 33bf1ca4 2023-04-08 benni
50 33bf1ca4 2023-04-08 benni if (sysctl (mib, 2, &diskcount, &len, NULL, 0) == -1)
51 44121ddf 2023-05-05 benni err (1, "sysctl(hw.diskcount)");
52 33bf1ca4 2023-04-08 benni
53 33bf1ca4 2023-04-08 benni return diskcount;
54 33bf1ca4 2023-04-08 benni }
55 33bf1ca4 2023-04-08 benni
56 33bf1ca4 2023-04-08 benni static char *disknames (void)
57 33bf1ca4 2023-04-08 benni {
58 33bf1ca4 2023-04-08 benni const int num = diskcount ();
59 33bf1ca4 2023-04-08 benni const int mib[2] = { CTL_HW, HW_DISKNAMES };
60 33bf1ca4 2023-04-08 benni size_t len = 32 * num;
61 33bf1ca4 2023-04-08 benni char *buffer = malloc (len);
62 33bf1ca4 2023-04-08 benni
63 33bf1ca4 2023-04-08 benni if (sysctl (mib, 2, buffer, &len, NULL, 0) == -1)
64 44121ddf 2023-05-05 benni err (1, "sysctl(hw.disknames)");
65 33bf1ca4 2023-04-08 benni
66 33bf1ca4 2023-04-08 benni return buffer;
67 33bf1ca4 2023-04-08 benni }
68 33bf1ca4 2023-04-08 benni
69 9c5d8ecc 2023-05-02 benni static char *stripdisk (char *n)
70 33bf1ca4 2023-04-08 benni {
71 9c5d8ecc 2023-05-02 benni const char sufx[] = " disk";
72 9c5d8ecc 2023-05-02 benni const size_t ln = strnlen (n, 16);
73 9c5d8ecc 2023-05-02 benni const size_t ls = sizeof sufx - 1;
74 9c5d8ecc 2023-05-02 benni
75 9c5d8ecc 2023-05-02 benni if (!memcmp (n + ln - ls, sufx, ls)) {
76 9c5d8ecc 2023-05-02 benni n[ln - ls] = '\0';
77 9c5d8ecc 2023-05-02 benni }
78 9c5d8ecc 2023-05-02 benni
79 9c5d8ecc 2023-05-02 benni return n;
80 9c5d8ecc 2023-05-02 benni }
81 9c5d8ecc 2023-05-02 benni
82 9c5d8ecc 2023-05-02 benni static void print_size (uint64_t sz)
83 9c5d8ecc 2023-05-02 benni {
84 33bf1ca4 2023-04-08 benni struct unit {
85 33bf1ca4 2023-04-08 benni char sym;
86 33bf1ca4 2023-04-08 benni uint64_t factor;
87 33bf1ca4 2023-04-08 benni };
88 33bf1ca4 2023-04-08 benni
89 33bf1ca4 2023-04-08 benni const struct unit units[] = {
90 33bf1ca4 2023-04-08 benni { 'P', 1ull << 50 },
91 33bf1ca4 2023-04-08 benni { 'T', 1ull << 40 },
92 33bf1ca4 2023-04-08 benni { 'G', 1ull << 30 },
93 33bf1ca4 2023-04-08 benni { 'M', 1ull << 20 },
94 33bf1ca4 2023-04-08 benni { 'K', 1ull << 10 },
95 33bf1ca4 2023-04-08 benni { '0', 0 },
96 33bf1ca4 2023-04-08 benni };
97 33bf1ca4 2023-04-08 benni
98 33bf1ca4 2023-04-08 benni char sym = 'B';
99 33bf1ca4 2023-04-08 benni uint64_t factor = 1;
100 33bf1ca4 2023-04-08 benni
101 33bf1ca4 2023-04-08 benni for (const struct unit *u = &units[0]; u->factor; ++u) {
102 33bf1ca4 2023-04-08 benni if (sz >= (u->factor * 9 / 10)) {
103 33bf1ca4 2023-04-08 benni sym = u->sym;
104 33bf1ca4 2023-04-08 benni factor = u->factor;
105 33bf1ca4 2023-04-08 benni break;
106 33bf1ca4 2023-04-08 benni }
107 33bf1ca4 2023-04-08 benni }
108 33bf1ca4 2023-04-08 benni
109 33bf1ca4 2023-04-08 benni const unsigned scaled10 = sz * 10 / factor;
110 33bf1ca4 2023-04-08 benni const unsigned scaled = sz / factor;
111 33bf1ca4 2023-04-08 benni if (scaled10 >= 1000) {
112 8a374bef 2023-04-08 benni printf ("%u", scaled);
113 33bf1ca4 2023-04-08 benni } else if (scaled10 >= 100) {
114 8a374bef 2023-04-08 benni printf (" %u", scaled);
115 33bf1ca4 2023-04-08 benni } else {
116 8a374bef 2023-04-08 benni printf ("%u.%u", scaled, scaled10 % 10);
117 33bf1ca4 2023-04-08 benni }
118 33bf1ca4 2023-04-08 benni
119 33bf1ca4 2023-04-08 benni putchar (sym);
120 9c5d8ecc 2023-05-02 benni putchar (' ');
121 33bf1ca4 2023-04-08 benni }
122 33bf1ca4 2023-04-08 benni
123 9c5d8ecc 2023-05-02 benni enum {
124 0c961f7d 2023-05-04 benni FIELD_NAME = 0x01,
125 1115ad3a 2023-05-05 benni FIELD_DUID = 0x02,
126 1115ad3a 2023-05-05 benni FIELD_SIZE = 0x04,
127 1115ad3a 2023-05-05 benni FIELD_USED = 0x08,
128 1115ad3a 2023-05-05 benni FIELD_FREE = 0x10,
129 1115ad3a 2023-05-05 benni FIELD_TYPE = 0x20,
130 1678d261 2023-05-15 benni FIELD_COMMENT = 0x40,
131 9c5d8ecc 2023-05-02 benni
132 1678d261 2023-05-15 benni FIELD_DEFAULT = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_COMMENT,
133 9c5d8ecc 2023-05-02 benni };
134 9c5d8ecc 2023-05-02 benni
135 0c961f7d 2023-05-04 benni enum {
136 0c961f7d 2023-05-04 benni OPT_NOHEADER = 0x01,
137 0c961f7d 2023-05-04 benni OPT_NOUNICODE = 0x02,
138 1678d261 2023-05-15 benni OPT_NOBIO = 0x04,
139 0c961f7d 2023-05-04 benni };
140 0c961f7d 2023-05-04 benni
141 b0a1b420 2023-05-15 benni struct my_diskinfo;
142 b0a1b420 2023-05-15 benni
143 9c5d8ecc 2023-05-02 benni struct my_partinfo {
144 1115ad3a 2023-05-05 benni char letter;
145 9c5d8ecc 2023-05-02 benni uint64_t size;
146 9c5d8ecc 2023-05-02 benni uint64_t fssize;
147 9c5d8ecc 2023-05-02 benni uint64_t free;
148 9c5d8ecc 2023-05-02 benni const char *fstype;
149 9c5d8ecc 2023-05-02 benni const char *mount;
150 1678d261 2023-05-15 benni const struct my_diskinfo *sub; // If this is part of a RAID
151 1678d261 2023-05-15 benni const char *raidstatus; // Only available if (sub != NULL)
152 9c5d8ecc 2023-05-02 benni };
153 9c5d8ecc 2023-05-02 benni
154 9c5d8ecc 2023-05-02 benni struct my_diskinfo {
155 9c5d8ecc 2023-05-02 benni char type[16];
156 9c5d8ecc 2023-05-02 benni char label[16];
157 69c9c7b9 2023-05-15 benni char name[8];
158 9c5d8ecc 2023-05-02 benni uint64_t size;
159 9c5d8ecc 2023-05-02 benni uint64_t used;
160 1115ad3a 2023-05-05 benni u_char duid[8];
161 b0a1b420 2023-05-15 benni uint8_t num_parts;
162 9c5d8ecc 2023-05-02 benni struct my_partinfo parts[MAXPARTITIONS];
163 1678d261 2023-05-15 benni const char *raidstatus; // If this is a RAID device
164 9c5d8ecc 2023-05-02 benni };
165 9c5d8ecc 2023-05-02 benni
166 7c38b724 2023-05-15 benni struct padding {
167 7c38b724 2023-05-15 benni int name;
168 7c38b724 2023-05-15 benni /* duid = 8 */
169 7c38b724 2023-05-15 benni /* size = 4 */
170 7c38b724 2023-05-15 benni /* used = 4 */
171 7c38b724 2023-05-15 benni /* free = 4 */
172 7c38b724 2023-05-15 benni int type;
173 7c38b724 2023-05-15 benni int comment;
174 7c38b724 2023-05-15 benni };
175 7c38b724 2023-05-15 benni
176 7c38b724 2023-05-15 benni static void print_header (int fields, const struct padding *p)
177 33bf1ca4 2023-04-08 benni {
178 9c5d8ecc 2023-05-02 benni if (fields & FIELD_NAME)
179 7c38b724 2023-05-15 benni printf ("%-*s ", p->name, "NAME");
180 9c5d8ecc 2023-05-02 benni
181 1115ad3a 2023-05-05 benni if (fields & FIELD_DUID)
182 1115ad3a 2023-05-05 benni printf ("%-18s ", "DUID");
183 1115ad3a 2023-05-05 benni
184 9c5d8ecc 2023-05-02 benni if (fields & FIELD_SIZE)
185 9c5d8ecc 2023-05-02 benni printf ("%-4s ", "SIZE");
186 9c5d8ecc 2023-05-02 benni
187 9c5d8ecc 2023-05-02 benni if (fields & FIELD_USED)
188 9c5d8ecc 2023-05-02 benni printf ("%-4s ", "USED");
189 9c5d8ecc 2023-05-02 benni
190 9c5d8ecc 2023-05-02 benni if (fields & FIELD_FREE)
191 9c5d8ecc 2023-05-02 benni printf ("%-4s ", "FREE");
192 9c5d8ecc 2023-05-02 benni
193 9c5d8ecc 2023-05-02 benni if (fields & FIELD_TYPE)
194 7c38b724 2023-05-15 benni printf ("%-*s ", p->type, "TYPE");
195 9c5d8ecc 2023-05-02 benni
196 1678d261 2023-05-15 benni if (fields & FIELD_COMMENT)
197 7c38b724 2023-05-15 benni printf ("%-*s ", p->comment, "COMMENT");
198 9c5d8ecc 2023-05-02 benni
199 7c38b724 2023-05-15 benni #if WSDEBUG
200 7c38b724 2023-05-15 benni putchar ('X');
201 7c38b724 2023-05-15 benni #endif
202 7c38b724 2023-05-15 benni
203 9c5d8ecc 2023-05-02 benni putchar ('\n');
204 33bf1ca4 2023-04-08 benni }
205 33bf1ca4 2023-04-08 benni
206 1115ad3a 2023-05-05 benni static void print_duid (const u_char *duid)
207 3103dec4 2023-05-01 benni {
208 1115ad3a 2023-05-05 benni for (size_t i = 0; i < 8; ++i) {
209 1115ad3a 2023-05-05 benni printf ("%02x", duid[i]);
210 1115ad3a 2023-05-05 benni }
211 1115ad3a 2023-05-05 benni }
212 1115ad3a 2023-05-05 benni
213 7c38b724 2023-05-15 benni static void print_disk (const struct my_diskinfo *, int, int, const char *, const struct padding *);
214 1115ad3a 2023-05-05 benni static void print_part (
215 1115ad3a 2023-05-05 benni const struct my_diskinfo *disk,
216 1115ad3a 2023-05-05 benni const struct my_partinfo *part,
217 1115ad3a 2023-05-05 benni int fields,
218 1115ad3a 2023-05-05 benni int options,
219 7c38b724 2023-05-15 benni bool last,
220 7c38b724 2023-05-15 benni const struct padding *p
221 1115ad3a 2023-05-05 benni ) {
222 0c961f7d 2023-05-04 benni if (fields & FIELD_NAME) {
223 0c961f7d 2023-05-04 benni const char *prefix = (options & OPT_NOUNICODE) ? " " : (last ? "└─" : "├─");
224 7c38b724 2023-05-15 benni printf (
225 7c38b724 2023-05-15 benni "%s%s%c%-*s ",
226 7c38b724 2023-05-15 benni prefix,
227 7c38b724 2023-05-15 benni disk->name,
228 7c38b724 2023-05-15 benni part->letter,
229 7c38b724 2023-05-15 benni p->name - 2 - (int)strlen (disk->name) - 1,
230 7c38b724 2023-05-15 benni ""
231 7c38b724 2023-05-15 benni );
232 0c961f7d 2023-05-04 benni }
233 3103dec4 2023-05-01 benni
234 1115ad3a 2023-05-05 benni if (fields & FIELD_DUID) {
235 1115ad3a 2023-05-05 benni print_duid (disk->duid);
236 1115ad3a 2023-05-05 benni printf (".%c ", part->letter);
237 1115ad3a 2023-05-05 benni }
238 1115ad3a 2023-05-05 benni
239 9c5d8ecc 2023-05-02 benni if (fields & FIELD_SIZE)
240 9c5d8ecc 2023-05-02 benni print_size (part->size);
241 9c5d8ecc 2023-05-02 benni
242 9c5d8ecc 2023-05-02 benni if (fields & FIELD_USED) {
243 9c5d8ecc 2023-05-02 benni if (part->fssize) {
244 9c5d8ecc 2023-05-02 benni print_size (part->fssize - part->free);
245 9c5d8ecc 2023-05-02 benni } else {
246 9c5d8ecc 2023-05-02 benni printf (" N/A ");
247 9c5d8ecc 2023-05-02 benni }
248 3103dec4 2023-05-01 benni }
249 3103dec4 2023-05-01 benni
250 9c5d8ecc 2023-05-02 benni if (fields & FIELD_FREE) {
251 9c5d8ecc 2023-05-02 benni if (part->fssize) {
252 9c5d8ecc 2023-05-02 benni print_size (part->free);
253 9c5d8ecc 2023-05-02 benni } else {
254 9c5d8ecc 2023-05-02 benni printf (" N/A ");
255 9c5d8ecc 2023-05-02 benni }
256 9c5d8ecc 2023-05-02 benni }
257 9c5d8ecc 2023-05-02 benni
258 9c5d8ecc 2023-05-02 benni if (fields & FIELD_TYPE)
259 7c38b724 2023-05-15 benni printf ("%-*s ", p->type, part->fstype);
260 9c5d8ecc 2023-05-02 benni
261 7c38b724 2023-05-15 benni if (fields & FIELD_COMMENT)
262 7c38b724 2023-05-15 benni printf ("%-*s ", p->comment, part->mount ? part->mount : "");
263 9c5d8ecc 2023-05-02 benni
264 7c38b724 2023-05-15 benni #if WSDEBUG
265 7c38b724 2023-05-15 benni putchar ('X');
266 7c38b724 2023-05-15 benni #endif
267 7c38b724 2023-05-15 benni
268 9c5d8ecc 2023-05-02 benni putchar ('\n');
269 b0a1b420 2023-05-15 benni
270 b0a1b420 2023-05-15 benni if (part->sub) {
271 7c38b724 2023-05-15 benni print_disk (part->sub, fields, options, part->raidstatus, p);
272 b0a1b420 2023-05-15 benni }
273 9c5d8ecc 2023-05-02 benni }
274 9c5d8ecc 2023-05-02 benni
275 7c38b724 2023-05-15 benni static void print_disk (
276 7c38b724 2023-05-15 benni const struct my_diskinfo *disk,
277 7c38b724 2023-05-15 benni int fields,
278 7c38b724 2023-05-15 benni int options,
279 7c38b724 2023-05-15 benni const char *raidstatus,
280 7c38b724 2023-05-15 benni const struct padding *p
281 7c38b724 2023-05-15 benni ) {
282 b0a1b420 2023-05-15 benni if (fields & FIELD_NAME) {
283 7e6c6845 2023-05-15 benni const char *prefix = raidstatus ? (options & OPT_NOUNICODE ? " " : "│ └─") : "";
284 7c38b724 2023-05-15 benni
285 7c38b724 2023-05-15 benni printf (
286 7c38b724 2023-05-15 benni "%s%-*s ",
287 7c38b724 2023-05-15 benni prefix,
288 7e6c6845 2023-05-15 benni p->name - (raidstatus ? 4 : 0),
289 7c38b724 2023-05-15 benni disk->name
290 7c38b724 2023-05-15 benni );
291 b0a1b420 2023-05-15 benni }
292 9c5d8ecc 2023-05-02 benni
293 1115ad3a 2023-05-05 benni if (fields & FIELD_DUID) {
294 1115ad3a 2023-05-05 benni print_duid (disk->duid);
295 1115ad3a 2023-05-05 benni printf (" ");
296 1115ad3a 2023-05-05 benni }
297 1115ad3a 2023-05-05 benni
298 9c5d8ecc 2023-05-02 benni if (fields & FIELD_SIZE)
299 9c5d8ecc 2023-05-02 benni print_size (disk->size);
300 9c5d8ecc 2023-05-02 benni
301 9c5d8ecc 2023-05-02 benni if (fields & FIELD_USED)
302 9c5d8ecc 2023-05-02 benni print_size (disk->used);
303 9c5d8ecc 2023-05-02 benni
304 9c5d8ecc 2023-05-02 benni if (fields & FIELD_FREE)
305 9c5d8ecc 2023-05-02 benni print_size (disk->size - disk->used);
306 9c5d8ecc 2023-05-02 benni
307 9c5d8ecc 2023-05-02 benni if (fields & FIELD_TYPE)
308 7c38b724 2023-05-15 benni printf ("%-*.16s ", p->type, disk->type);
309 9c5d8ecc 2023-05-02 benni
310 1678d261 2023-05-15 benni if (fields & FIELD_COMMENT) {
311 1678d261 2023-05-15 benni if (raidstatus) {
312 7c38b724 2023-05-15 benni printf ("%-*s ", p->comment, raidstatus);
313 1678d261 2023-05-15 benni } else if (disk->raidstatus) {
314 7c38b724 2023-05-15 benni printf (
315 7c38b724 2023-05-15 benni "%.16s (%s)%*s ",
316 7c38b724 2023-05-15 benni disk->label,
317 7c38b724 2023-05-15 benni disk->raidstatus,
318 7c38b724 2023-05-15 benni p->comment - (int)strnlen (disk->label, sizeof disk->label) - (int)strlen (disk->raidstatus) - 3,
319 7c38b724 2023-05-15 benni ""
320 7c38b724 2023-05-15 benni );
321 1678d261 2023-05-15 benni } else {
322 7c38b724 2023-05-15 benni printf ("%-*.16s ", p->comment, disk->label);
323 1678d261 2023-05-15 benni }
324 1678d261 2023-05-15 benni }
325 9c5d8ecc 2023-05-02 benni
326 7c38b724 2023-05-15 benni #if WSDEBUG
327 7c38b724 2023-05-15 benni putchar ('X');
328 7c38b724 2023-05-15 benni #endif
329 7c38b724 2023-05-15 benni
330 9c5d8ecc 2023-05-02 benni putchar ('\n');
331 9c5d8ecc 2023-05-02 benni
332 1678d261 2023-05-15 benni if (!raidstatus) {
333 b0a1b420 2023-05-15 benni for (uint8_t i = 0; i < disk->num_parts; ++i)
334 7c38b724 2023-05-15 benni print_part (disk, &disk->parts[i], fields, options, i == (disk->num_parts - 1), p);
335 b0a1b420 2023-05-15 benni }
336 9c5d8ecc 2023-05-02 benni }
337 9c5d8ecc 2023-05-02 benni
338 9c5d8ecc 2023-05-02 benni static const struct statfs *find_mount (const char *dev)
339 9c5d8ecc 2023-05-02 benni {
340 9c5d8ecc 2023-05-02 benni static struct statfs *mounts = NULL;
341 9c5d8ecc 2023-05-02 benni static int n_mounts;
342 9c5d8ecc 2023-05-02 benni
343 9c5d8ecc 2023-05-02 benni if (!mounts) {
344 9c5d8ecc 2023-05-02 benni n_mounts = getmntinfo (&mounts, MNT_NOWAIT);
345 9c5d8ecc 2023-05-02 benni if (n_mounts == 0)
346 44121ddf 2023-05-05 benni err (1, "getmntinfo()");
347 9c5d8ecc 2023-05-02 benni }
348 9c5d8ecc 2023-05-02 benni
349 9c5d8ecc 2023-05-02 benni for (int i = 0; i < n_mounts; ++i) {
350 9c5d8ecc 2023-05-02 benni if (!strcmp (dev, mounts[i].f_mntfromname))
351 9c5d8ecc 2023-05-02 benni return &mounts[i];
352 9c5d8ecc 2023-05-02 benni }
353 9c5d8ecc 2023-05-02 benni
354 9c5d8ecc 2023-05-02 benni return NULL;
355 3103dec4 2023-05-01 benni }
356 3103dec4 2023-05-01 benni
357 9c5d8ecc 2023-05-02 benni static struct my_diskinfo read_disk (const char *name)
358 9c5d8ecc 2023-05-02 benni {
359 9c5d8ecc 2023-05-02 benni struct my_diskinfo disk;
360 9c5d8ecc 2023-05-02 benni struct disklabel label;
361 0c422a5b 2023-05-05 benni char *ppath, *letter;
362 9c5d8ecc 2023-05-02 benni
363 9c5d8ecc 2023-05-02 benni bzero (&disk, sizeof disk);
364 9c5d8ecc 2023-05-02 benni
365 9c5d8ecc 2023-05-02 benni { // Read disklabel.
366 0c422a5b 2023-05-05 benni size_t len;
367 9c5d8ecc 2023-05-02 benni int fd;
368 9c5d8ecc 2023-05-02 benni
369 0c422a5b 2023-05-05 benni fd = opendev (name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, &ppath);
370 9c5d8ecc 2023-05-02 benni if (fd < 0)
371 44121ddf 2023-05-05 benni err (1, "opendev(%s)", name);
372 0c422a5b 2023-05-05 benni
373 9c5d8ecc 2023-05-02 benni if (ioctl (fd, DIOCGDINFO, &label) < 0)
374 44121ddf 2023-05-05 benni err (1, "ioctl(%s, DIOCGDINFO)", name);
375 9c5d8ecc 2023-05-02 benni close (fd);
376 9c5d8ecc 2023-05-02 benni
377 0c422a5b 2023-05-05 benni len = strlen (ppath);
378 0c422a5b 2023-05-05 benni letter = ppath + len - 1;
379 0c422a5b 2023-05-05 benni }
380 5691dc7b 2023-05-05 benni
381 69c9c7b9 2023-05-15 benni strlcpy (disk.name, name, sizeof disk.name);
382 69c9c7b9 2023-05-15 benni disk.size = DL_GETDSIZE (&label) * label.d_secsize;
383 69c9c7b9 2023-05-15 benni memcpy (disk.type, label.d_typename, sizeof disk.type);
384 9c5d8ecc 2023-05-02 benni stripdisk (disk.type);
385 69c9c7b9 2023-05-15 benni memcpy (disk.label, label.d_packname, sizeof disk.label);
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 185836d0 2023-05-15 benni if (len_vendor < 4 || len_vendor > 8) {
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 185836d0 2023-05-15 benni char *last = disk + strlen (disk) - 1;
648 185836d0 2023-05-15 benni if (isalpha (*last)) {
649 185836d0 2023-05-15 benni warnx ("%s: specifying a partition is not supported, using the drive.", disk);
650 185836d0 2023-05-15 benni *last = '\0';
651 185836d0 2023-05-15 benni }
652 5691dc7b 2023-05-05 benni disks[num_disks++] = read_disk (disk);
653 5691dc7b 2023-05-05 benni }
654 33bf1ca4 2023-04-08 benni }
655 33bf1ca4 2023-04-08 benni
656 33bf1ca4 2023-04-08 benni free (names);
657 9c5d8ecc 2023-05-02 benni
658 4d8e470c 2023-05-15 benni mergesort (disks, num_disks, sizeof *disks, compare_disk);
659 4d8e470c 2023-05-15 benni
660 1678d261 2023-05-15 benni if (!(options & OPT_NOBIO)) {
661 1678d261 2023-05-15 benni for (size_t i = 0; i < num_disks; ++i) {
662 1678d261 2023-05-15 benni read_raid (&disks[i], disks, num_disks);
663 1678d261 2023-05-15 benni }
664 b0a1b420 2023-05-15 benni }
665 b0a1b420 2023-05-15 benni
666 7c38b724 2023-05-15 benni struct padding p = {
667 7c38b724 2023-05-15 benni .name = strlen ("NAME"),
668 7c38b724 2023-05-15 benni .type = strlen ("TYPE"),
669 7c38b724 2023-05-15 benni .comment = strlen ("COMMENT"),
670 7c38b724 2023-05-15 benni };
671 7c38b724 2023-05-15 benni
672 7c38b724 2023-05-15 benni for (size_t i = 0; i < num_disks; ++i)
673 7c38b724 2023-05-15 benni pad_disk (&p, &disks[i]);
674 7c38b724 2023-05-15 benni
675 0c961f7d 2023-05-04 benni if (!(options & OPT_NOHEADER))
676 7c38b724 2023-05-15 benni print_header (fields, &p);
677 9c5d8ecc 2023-05-02 benni
678 9c5d8ecc 2023-05-02 benni for (size_t i = 0; i < num_disks; ++i) {
679 7c38b724 2023-05-15 benni print_disk (&disks[i], fields, options, NULL, &p);
680 9c5d8ecc 2023-05-02 benni }
681 5691dc7b 2023-05-05 benni
682 33bf1ca4 2023-04-08 benni return 0;
683 33bf1ca4 2023-04-08 benni }