gcontini
2020-10-25 64c956947f61f7d213ed00a7afa403fb591f3cd8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#include <paths.h>
#include <sys/stat.h>
#include <stdio.h>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <string>
#include <stdio.h>
#include <string.h>
#include <sstream>
#include "../os.h"
#include "../../base/logger.h"
 
#include <mntent.h>
#include <dirent.h>
#include <sys/utsname.h>
#ifndef NDEBUG
#include <valgrind/memcheck.h>
#endif
 
//#ifdef USE_DISK_MODEL
///#define PARSE_ID_FUNC parse_disk_id
//#define ID_FOLDER "/dev/disk/by-id"
//#else
#define PARSE_ID_FUNC parseUUID
#define ID_FOLDER "/dev/disk/by-uuid"
//#endif
#ifdef USE_DBUS
#include <dbus-1.0/dbus/dbus.h>
#endif
 
/**
 *Usually uuid are hex number separated by "-". this method read up to 8 hex
 *numbers skipping - characters.
 *@param uuid uuid as read in /dev/disk/by-uuid
 *@param buffer_out: unsigned char buffer[8] output buffer for result
 */
static void parseUUID(const char *uuid, unsigned char *buffer_out, unsigned int out_size) {
    unsigned int i, j;
    char *hexuuid;
    unsigned char cur_character;
    // remove characters not in hex set
    size_t len = strlen(uuid);
    hexuuid = (char *)malloc(sizeof(char) * len);
    memset(buffer_out, 0, out_size);
    memset(hexuuid, 0, sizeof(char) * len);
 
    for (i = 0, j = 0; i < len; i++) {
        if (isxdigit(uuid[i])) {
            hexuuid[j] = uuid[i];
            j++;
        } else {
            // skip
            continue;
        }
    }
    if (j % 2 == 1) {
        hexuuid[j++] = '0';
    }
    hexuuid[j] = '\0';
    for (i = 0; i < j / 2; i++) {
        sscanf(&hexuuid[i * 2], "%2hhx", &cur_character);
        buffer_out[i % out_size] = buffer_out[i % out_size] ^ cur_character;
    }
 
    free(hexuuid);
}
 
static void parse_disk_id(const char *uuid, unsigned char *buffer_out, size_t out_size) {
    unsigned int i;
    size_t len = strlen(uuid);
    memset(buffer_out, 0, out_size);
    for (i = 0; i < len; i++) {
        buffer_out[i % out_size] = buffer_out[i % out_size] ^ uuid[i];
    }
}
 
/**
 *     int id;
    char device[MAX_PATH];
    unsigned char disk_sn[8];
    char label[255];
    int preferred;
 * @param blkidfile
 * @param diskInfos_out
 * @return
 */
 
static std::string getAttribute(const std::string &source, const std::string &attrName) {
    std::string attr_namefull = attrName + "=\"";
    std::size_t startpos = source.find(attr_namefull) + attr_namefull.size();
    std::size_t endpos = source.find("\"", startpos);
    return source.substr(startpos, endpos - startpos);
}
 
FUNCTION_RETURN parse_blkid(const std::string &blkid_file_content, std::vector<DiskInfo> &diskInfos_out,
                            std::unordered_map<std::string, int> &disk_by_uuid) {
    DiskInfo diskInfo;
    int diskNum = 0;
    for (std::size_t oldpos = 0, pos = 0; (pos = blkid_file_content.find("</device>", oldpos)) != std::string::npos;
         oldpos = pos + 1) {
        std::string cur_dev = blkid_file_content.substr(oldpos, pos);
        diskInfo.id = diskNum++;
        std::string device = cur_dev.substr(cur_dev.find_last_of(">") + 1);
        strncpy(diskInfo.device, device.c_str(), MAX_PATH);
        std::string label = getAttribute(cur_dev, "PARTLABEL");
        strncpy(diskInfo.label, label.c_str(), 255);
        std::string disk_sn = getAttribute(cur_dev, "UUID");
        parseUUID(disk_sn.c_str(), diskInfo.disk_sn, sizeof(diskInfo.disk_sn));
        std::string disk_type = getAttribute(cur_dev, "TYPE");
        disk_by_uuid.insert(std::pair<std::string, int>(disk_sn, diskInfo.id));
        // unlikely that somebody put the swap on a removable disk.
        // this is a first rough guess on what can be a preferred disk for blkid devices
        // just in case /etc/fstab can't be accessed or it is not up to date.
        diskInfo.preferred = (disk_type == "swap");
        diskInfos_out.push_back(diskInfo);
    }
    return FUNCTION_RETURN::FUNC_RET_OK;
}
 
#define BLKID_LOCATIONS {"/run/blkid/blkid.tab", "/etc/blkid.tab"};
 
static FUNCTION_RETURN getDiskInfos_blkid(std::vector<DiskInfo> &diskInfos,
                                          std::unordered_map<std::string, int> &disk_by_uuid) {
    const char *strs[] = BLKID_LOCATIONS;
    bool can_read = false;
    std::stringstream buffer;
    for (int i = 0; i < sizeof(strs) / sizeof(const char *); i++) {
        const char *location = strs[i];
        std::ifstream t(location);
        if (t.is_open()) {
            buffer << t.rdbuf();
            can_read = true;
            break;
        }
    }
    if (!can_read) {
        return FUNCTION_RETURN::FUNC_RET_NOT_AVAIL;
    }
 
    return parse_blkid(buffer.str(), diskInfos, disk_by_uuid);
}
 
#define MAX_UNITS 40
 
static void read_disk_labels(std::vector<DiskInfo> &disk_infos) {
    struct stat sym_stat;
    struct dirent *dir;
 
    std::string label_dir("/dev/disk/by-label");
    DIR *disk_by_label = opendir(label_dir.c_str());
    if (disk_by_label == nullptr) {
        label_dir = "/dev/disk/by-partlabel";
        disk_by_label = opendir(label_dir.c_str());
    }
    if (disk_by_label != nullptr) {
        while ((dir = readdir(disk_by_label)) != nullptr) {
            if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
                continue;
            }
            std::string cur_disk_label = label_dir + "/" + dir->d_name;
            if (stat(cur_disk_label.c_str(), &sym_stat) == 0) {
                bool found = false;
                for (auto &diskInfo : disk_infos) {
                    if (((int)(sym_stat.st_ino)) == diskInfo.id) {
                        strncpy(diskInfo.label, dir->d_name, 255 - 1);
                        diskInfo.label_initialized = true;
                        LOG_DEBUG("Label for disk ino %d device %s, set to %s", sym_stat.st_ino, diskInfo.device,
                                  diskInfo.label);
                        break;
                    }
                }
            } else {
                LOG_DEBUG("Stat %s for fail:F %s", cur_disk_label.c_str(), std::strerror(errno));
            }
        }
        closedir(disk_by_label);
    } else {
        LOG_DEBUG("Open %s for reading disk labels fail: %s", label_dir.c_str(), std::strerror(errno));
    }
}
 
FUNCTION_RETURN getDiskInfos_dev(std::vector<DiskInfo> &disk_infos,
                                 std::unordered_map<std::string, int> &disk_by_uuid) {
    struct dirent *dir = NULL;
    struct stat sym_stat;
    FUNCTION_RETURN result;
    char device_name[MAX_PATH];
 
    DIR *disk_by_uuid_dir = opendir(ID_FOLDER);
    if (disk_by_uuid_dir == nullptr) {
        LOG_DEBUG("Open " ID_FOLDER " fail: %s", std::strerror(errno));
    } else {
        const std::string base_dir(ID_FOLDER "/");
        while ((dir = readdir(disk_by_uuid_dir)) != nullptr && disk_infos.size() < MAX_UNITS) {
            if (::strcmp(dir->d_name, ".") == 0 || ::strcmp(dir->d_name, "..") == 0 ||
                ::strncmp(dir->d_name, "usb", 3) == 0) {
                continue;
            }
 
            std::string cur_dir = base_dir + dir->d_name;
            if (stat(cur_dir.c_str(), &sym_stat) == 0) {
                DiskInfo tmpDiskInfo;
                tmpDiskInfo.id = sym_stat.st_ino;
                ssize_t len = ::readlink(cur_dir.c_str(), device_name, MAX_PATH - 1);
                if (len != -1) {
                    device_name[len] = '\0';
                    std::string device_name_s(device_name, len);
                    auto pos = device_name_s.find_last_of("/");
                    if (pos != std::string::npos) {
                        device_name_s = device_name_s.substr(pos + 1);
                    }
                    strncpy(tmpDiskInfo.device, device_name_s.c_str(), sizeof(tmpDiskInfo.device));
                    PARSE_ID_FUNC(dir->d_name, tmpDiskInfo.disk_sn, sizeof(tmpDiskInfo.disk_sn));
                    tmpDiskInfo.sn_initialized = true;
                    tmpDiskInfo.label_initialized = false;
                    tmpDiskInfo.preferred = false;
                    bool found = false;
                    for (auto diskInfo : disk_infos) {
                        if (tmpDiskInfo.id == diskInfo.id) {
                            found = true;
                            break;
                        }
                    }
                    disk_by_uuid.insert(std::pair<std::string, int>(std::string(dir->d_name), tmpDiskInfo.id));
                    if (!found) {
                        LOG_DEBUG("Found disk inode %d device %s, sn %s", sym_stat.st_ino, tmpDiskInfo.device,
                                  dir->d_name);
                        disk_infos.push_back(tmpDiskInfo);
                    }
                } else {
                    LOG_DEBUG("Error %s during readlink of %s", std::strerror(errno), cur_dir.c_str());
                }
            } else {
                LOG_DEBUG("Error %s during stat of %s", std::strerror(errno), cur_dir.c_str());
            }
        }
        closedir(disk_by_uuid_dir);
    }
 
    result = disk_infos.size() > 0 ? FUNCTION_RETURN::FUNC_RET_OK : FUNCTION_RETURN::FUNC_RET_NOT_AVAIL;
    read_disk_labels(disk_infos);
    return result;
}
 
/**
 * Try to determine removable devices: as a first guess removable devices doesn't have
 * an entry in /etc/fstab
 *
 * @param diskInfos
 */
static void set_preferred_disks(std::vector<DiskInfo> &diskInfos, std::unordered_map<std::string, int> &disk_by_uuid) {
    FILE *fstabFile = setmntent("/etc/fstab", "r");
    if (fstabFile == nullptr) {
        LOG_DEBUG("/etc/fstab not accessible");
        return;
    }
    struct mntent *ent;
    while (nullptr != (ent = getmntent(fstabFile))) {
        bool found = false;
        std::string device_name_s(ent->mnt_fsname);
        LOG_DEBUG("found fstab entry %s ", ent->mnt_fsname);
        if (strncmp("UUID=", ent->mnt_fsname, 5) == 0) {
            // fstab entry is uuid
            device_name_s = device_name_s.substr(5);
            auto it = disk_by_uuid.find(device_name_s);
            if (it != disk_by_uuid.end()) {
                for (auto &disk_info : diskInfos) {
                    if (it->second == disk_info.id) {
                        disk_info.preferred = true;
                        LOG_DEBUG("Disk %d device %s set as preferred", disk_info.id, disk_info.device);
                        break;
                    }
                }
            } else {
                LOG_DEBUG("fstab device %s found, but no corresponding diskInfo", ent->mnt_fsname);
            }
        } else {
            // fstab entry is a device
            auto pos = device_name_s.find_last_of("/");
            if (pos != std::string::npos) {
                device_name_s = device_name_s.substr(pos + 1);
            }
 
            for (auto disk_info : diskInfos) {
                if (device_name_s == disk_info.device) {
                    disk_info.preferred = true;
                    LOG_DEBUG("Disk %d device %s set as preferred", disk_info.id, disk_info.device);
                    break;
                }
            }
        }
    }
    endmntent(fstabFile);
    return;
}
 
/**
 * First try to read disk_infos from /dev/disk/by-uuid folder, if fails try to use
 * blkid cache to see what's in there, then try to exclude removable disks
 * looking at /etc/fstab
 * @param diskInfos_out vector used to output the disk informations
 * @return
 */
FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo> &disk_infos) {
    std::unordered_map<std::string, int> disk_by_uuid;
 
    FUNCTION_RETURN result = getDiskInfos_dev(disk_infos, disk_by_uuid);
 
    if (result != FUNCTION_RETURN::FUNC_RET_OK) {
        result = getDiskInfos_blkid(disk_infos, disk_by_uuid);
    }
    if (result == FUNCTION_RETURN::FUNC_RET_OK) {
        set_preferred_disks(disk_infos, disk_by_uuid);
    }
    return result;
}
 
FUNCTION_RETURN getMachineName(unsigned char identifier[6]) {
    static struct utsname u;
 
    if (uname(&u) < 0) {
        return FUNC_RET_ERROR;
    }
    memcpy(identifier, u.nodename, 6);
    return FUNC_RET_OK;
}
 
FUNCTION_RETURN getOsSpecificIdentifier(unsigned char identifier[6]) {
#if USE_DBUS
    char *dbus_id = dbus_get_local_machine_id();
    if (dbus_id == NULL) {
        return FUNC_RET_ERROR;
    }
    memcpy(identifier, dbus_id, 6);
    dbus_free(dbus_id);
    return FUNC_RET_OK;
#else
    return FUNC_RET_NOT_AVAIL;
#endif
}
 
FUNCTION_RETURN getModuleName(char buffer[MAX_PATH]) {
    FUNCTION_RETURN result;
    char path[MAX_PATH] = {0};
    char proc_path[MAX_PATH], pidStr[64];
    pid_t pid = getpid();
    sprintf(pidStr, "%d", pid);
    strcpy(proc_path, "/proc/");
    strcat(proc_path, pidStr);
    strcat(proc_path, "/exe");
 
    int ch = readlink(proc_path, path, MAX_PATH - 1);
    if (ch != -1) {
        path[ch] = '\0';
        strncpy(buffer, path, ch);
        result = FUNC_RET_OK;
    } else {
        result = FUNC_RET_ERROR;
    }
    return result;
}