]>
Commit | Line | Data |
---|---|---|
f0fa1795 MT |
1 | /*############################################################################# |
2 | # # | |
3 | # IPFire - An Open Source Firewall Distribution # | |
4 | # Copyright (C) 2014 IPFire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | #include <assert.h> | |
d7dd283b MT |
22 | #include <blkid/blkid.h> |
23 | #include <fcntl.h> | |
f0fa1795 | 24 | #include <libudev.h> |
d7dd283b | 25 | #include <math.h> |
f0fa1795 MT |
26 | #include <stdio.h> |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
d7dd283b | 29 | #include <sys/ioctl.h> |
f0fa1795 MT |
30 | #include <sys/mount.h> |
31 | #include <unistd.h> | |
32 | ||
d7dd283b MT |
33 | #include <linux/fs.h> |
34 | ||
f0fa1795 MT |
35 | #include "hw.h" |
36 | ||
37 | struct hw* hw_init() { | |
38 | struct hw* hw = malloc(sizeof(*hw)); | |
39 | assert(hw); | |
40 | ||
41 | // Initialize libudev | |
42 | hw->udev = udev_new(); | |
43 | if (!hw->udev) { | |
44 | fprintf(stderr, "Could not create udev instance\n"); | |
45 | exit(1); | |
46 | } | |
47 | ||
48 | return hw; | |
49 | } | |
50 | ||
51 | void hw_free(struct hw* hw) { | |
52 | if (hw->udev) | |
53 | udev_unref(hw->udev); | |
54 | ||
55 | free(hw); | |
56 | } | |
57 | ||
58 | static int strstartswith(const char* a, const char* b) { | |
59 | return (strncmp(a, b, strlen(b)) == 0); | |
60 | } | |
61 | ||
62 | int hw_mount(const char* source, const char* target, int flags) { | |
63 | return mount(source, target, "iso9660", flags, NULL); | |
64 | } | |
65 | ||
66 | int hw_umount(const char* target) { | |
67 | return umount2(target, MNT_DETACH); | |
68 | } | |
69 | ||
70 | static int hw_test_source_medium(const char* path) { | |
71 | int ret = hw_mount(path, SOURCE_MOUNT_PATH, MS_RDONLY); | |
72 | ||
73 | // If the source could not be mounted we | |
74 | // cannot proceed. | |
75 | if (ret) | |
76 | return ret; | |
77 | ||
78 | // Check if the test file exists. | |
79 | ret = access(SOURCE_TEST_FILE, F_OK); | |
80 | ||
81 | // Umount the test device. | |
82 | hw_umount(SOURCE_MOUNT_PATH); | |
83 | ||
84 | return ret; | |
85 | } | |
86 | ||
87 | char* hw_find_source_medium(struct hw* hw) { | |
88 | char* ret = NULL; | |
89 | ||
90 | struct udev_enumerate* enumerate = udev_enumerate_new(hw->udev); | |
91 | ||
92 | udev_enumerate_add_match_subsystem(enumerate, "block"); | |
93 | udev_enumerate_scan_devices(enumerate); | |
94 | ||
95 | struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate); | |
96 | ||
97 | struct udev_list_entry* dev_list_entry; | |
98 | udev_list_entry_foreach(dev_list_entry, devices) { | |
99 | const char* path = udev_list_entry_get_name(dev_list_entry); | |
100 | struct udev_device* dev = udev_device_new_from_syspath(hw->udev, path); | |
101 | ||
102 | const char* dev_path = udev_device_get_devnode(dev); | |
103 | ||
104 | // Skip everything what we cannot work with | |
105 | if (strstartswith(dev_path, "/dev/loop") || strstartswith(dev_path, "/dev/fd") || | |
106 | strstartswith(dev_path, "/dev/ram")) | |
107 | continue; | |
108 | ||
109 | if (hw_test_source_medium(dev_path)) { | |
110 | ret = strdup(dev_path); | |
111 | } | |
112 | ||
113 | udev_device_unref(dev); | |
114 | ||
115 | // If a suitable device was found the search will end. | |
116 | if (ret) | |
117 | break; | |
118 | } | |
119 | ||
120 | udev_enumerate_unref(enumerate); | |
121 | ||
122 | return ret; | |
123 | } | |
d7dd283b MT |
124 | |
125 | static struct hw_disk** hw_create_disks() { | |
126 | struct hw_disk** ret = malloc(sizeof(*ret) * (HW_MAX_DISKS + 1)); | |
127 | ||
128 | return ret; | |
129 | } | |
130 | ||
131 | static unsigned long long hw_block_device_get_size(const char* dev) { | |
132 | int fd = open(dev, O_RDONLY); | |
133 | if (fd < 0) | |
134 | return 0; | |
135 | ||
136 | unsigned long long size = blkid_get_dev_size(fd); | |
137 | close(fd); | |
138 | ||
139 | return size; | |
140 | } | |
141 | ||
142 | struct hw_disk** hw_find_disks(struct hw* hw) { | |
143 | struct hw_disk** ret = hw_create_disks(); | |
144 | struct hw_disk** disks = ret; | |
145 | ||
146 | struct udev_enumerate* enumerate = udev_enumerate_new(hw->udev); | |
147 | ||
148 | udev_enumerate_add_match_subsystem(enumerate, "block"); | |
149 | udev_enumerate_scan_devices(enumerate); | |
150 | ||
151 | struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate); | |
152 | ||
153 | struct udev_list_entry* dev_list_entry; | |
154 | unsigned int i = HW_MAX_DISKS; | |
155 | udev_list_entry_foreach(dev_list_entry, devices) { | |
156 | const char* path = udev_list_entry_get_name(dev_list_entry); | |
157 | struct udev_device* dev = udev_device_new_from_syspath(hw->udev, path); | |
158 | ||
159 | const char* dev_path = udev_device_get_devnode(dev); | |
160 | ||
161 | // Skip everything what we cannot work with | |
162 | if (strstartswith(dev_path, "/dev/loop") || strstartswith(dev_path, "/dev/fd") || | |
163 | strstartswith(dev_path, "/dev/ram") || strstartswith(dev_path, "/dev/sr")) { | |
164 | udev_device_unref(dev); | |
165 | continue; | |
166 | } | |
167 | ||
168 | // DEVTYPE must be disk (otherwise we will see all sorts of partitions here) | |
169 | const char* devtype = udev_device_get_property_value(dev, "DEVTYPE"); | |
170 | if (devtype && (strcmp(devtype, "disk") != 0)) { | |
171 | udev_device_unref(dev); | |
172 | continue; | |
173 | } | |
174 | ||
175 | // Skip all source mediums | |
176 | if (hw_test_source_medium(dev_path) == 0) { | |
177 | udev_device_unref(dev); | |
178 | continue; | |
179 | } | |
180 | ||
181 | // Skip devices with a size of zero | |
182 | unsigned long long size = hw_block_device_get_size(dev_path); | |
183 | if (size == 0) { | |
184 | udev_device_unref(dev); | |
185 | continue; | |
186 | } | |
187 | ||
188 | struct hw_disk* disk = malloc(sizeof(*disk)); | |
189 | if (disk == NULL) | |
190 | return NULL; | |
191 | ||
192 | disk->ref = 1; | |
193 | ||
194 | strncpy(disk->path, dev_path, sizeof(disk->path)); | |
195 | ||
196 | disk->size = size; | |
197 | ||
198 | // Vendor | |
199 | const char* vendor = udev_device_get_property_value(dev, "ID_VENDOR"); | |
200 | if (!vendor) | |
201 | vendor = udev_device_get_sysattr_value(dev, "vendor"); | |
202 | if (!vendor) | |
203 | vendor = udev_device_get_sysattr_value(dev, "manufacturer"); | |
204 | if (!vendor) | |
205 | vendor = "N/A"; | |
206 | ||
207 | strncpy(disk->vendor, vendor, sizeof(disk->vendor)); | |
208 | ||
209 | // Model | |
210 | const char* model = udev_device_get_property_value(dev, "ID_MODEL"); | |
211 | if (!model) | |
212 | model = udev_device_get_sysattr_value(dev, "model"); | |
213 | if (!model) | |
214 | model = udev_device_get_sysattr_value(dev, "product"); | |
215 | if (!model) | |
216 | model = "N/A"; | |
217 | ||
218 | strncpy(disk->model, model, sizeof(disk->model)); | |
219 | ||
220 | snprintf(disk->description, sizeof(disk->description), | |
221 | "%4.1fGB %s - %s", (double)disk->size / pow(1024, 3), | |
222 | disk->vendor, disk->model); | |
223 | ||
224 | *disks++ = disk; | |
225 | ||
226 | if (--i == 0) | |
227 | break; | |
228 | ||
229 | udev_device_unref(dev); | |
230 | } | |
231 | ||
232 | udev_enumerate_unref(enumerate); | |
233 | ||
234 | *disks = NULL; | |
235 | ||
236 | return ret; | |
237 | } | |
238 | ||
239 | void hw_free_disks(struct hw_disk** disks) { | |
240 | struct hw_disk** disk = disks; | |
241 | ||
242 | while (*disk != NULL) { | |
243 | if (--(*disk)->ref == 0) | |
244 | free(*disk); | |
245 | ||
246 | disk++; | |
247 | } | |
248 | ||
249 | free(disks); | |
250 | } | |
251 | ||
252 | unsigned int hw_count_disks(struct hw_disk** disks) { | |
253 | unsigned int ret = 0; | |
254 | ||
255 | while (*disks++) | |
256 | ret++; | |
257 | ||
258 | return ret; | |
259 | } | |
260 | ||
261 | struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) { | |
262 | struct hw_disk** ret = hw_create_disks(); | |
263 | struct hw_disk** selected_disks = ret; | |
264 | ||
265 | unsigned int num_disks = hw_count_disks(disks); | |
266 | ||
267 | for (unsigned int i = 0; i < num_disks; i++) { | |
268 | if (selection && selection[i]) { | |
269 | struct hw_disk *selected_disk = disks[i]; | |
270 | selected_disk->ref++; | |
271 | ||
272 | *selected_disks++ = selected_disk; | |
273 | } | |
274 | } | |
275 | ||
276 | // Set sentinel | |
277 | *selected_disks = NULL; | |
278 | ||
279 | return ret; | |
280 | } | |
281 | ||
282 | struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks) { | |
283 | struct hw_destination* dest = malloc(sizeof(*dest)); | |
284 | ||
285 | if (part_type == HW_PART_TYPE_NORMAL) { | |
286 | dest->disk1 = *disks; | |
287 | dest->disk2 = NULL; | |
288 | ||
289 | strncpy(dest->path, dest->disk1->path, sizeof(dest->path)); | |
290 | ||
291 | } else if (part_type == HW_PART_TYPE_RAID1) { | |
292 | dest->disk1 = *disks++; | |
293 | dest->disk2 = *disks; | |
294 | ||
295 | snprintf(dest->path, sizeof(dest->path), "/dev/md0"); | |
296 | } | |
297 | ||
298 | // Is this a RAID device? | |
299 | dest->is_raid = (part_type > HW_PART_TYPE_NORMAL); | |
300 | ||
301 | // Set partition names | |
302 | char path[DEV_SIZE]; | |
303 | snprintf(path, sizeof(path), "%s%s", dest->path, (dest->is_raid) ? "p" : ""); | |
304 | snprintf(dest->part_boot, sizeof(dest->part_boot), "%s1", path); | |
305 | snprintf(dest->part_swap, sizeof(dest->part_swap), "%s2", path); | |
306 | snprintf(dest->part_root, sizeof(dest->part_root), "%s3", path); | |
307 | snprintf(dest->part_data, sizeof(dest->part_data), "%s4", path); | |
308 | ||
309 | if (dest->is_raid) { | |
310 | dest->size = (dest->disk1->size >= dest->disk2->size) ? | |
311 | dest->disk1->size : dest->disk2->size; | |
312 | } else { | |
313 | dest->size = dest->disk1->size; | |
314 | } | |
315 | ||
316 | return dest; | |
317 | } | |
c4e96674 MT |
318 | |
319 | unsigned long long hw_memory() { | |
320 | FILE* handle = NULL; | |
321 | char line[STRING_SIZE]; | |
322 | ||
323 | unsigned long long memory = 0; | |
324 | ||
325 | /* Calculate amount of memory in machine */ | |
326 | if ((handle = fopen("/proc/meminfo", "r"))) { | |
327 | while (fgets(line, sizeof(line), handle)) { | |
328 | if (!sscanf (line, "MemTotal: %llu kB", memory)) { | |
329 | memory = 0; | |
330 | } | |
331 | } | |
332 | ||
333 | fclose(handle); | |
334 | } | |
335 | ||
336 | return memory * 1024; | |
337 | } |