Line data Source code
1 : /*
2 : * Copyright (C) 2014-2024 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Tomas Bzatek <tbzatek@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <string.h>
22 : #include <stdio.h>
23 : #include <unistd.h>
24 : #include <errno.h>
25 :
26 : #include "smart.h"
27 : #include "smart-private.h"
28 :
29 :
30 13 : void free_drivedb_attrs (DriveDBAttr **attrs) {
31 : DriveDBAttr **a;
32 :
33 13 : if (attrs == NULL)
34 9 : return;
35 132 : for (a = attrs; *a; a++) {
36 128 : g_free ((*a)->name);
37 128 : g_free (*a);
38 : }
39 4 : g_free (attrs);
40 : }
41 :
42 :
43 : #ifndef HAVE_DRIVEDB_H
44 : DriveDBAttr** drivedb_lookup_drive (G_GNUC_UNUSED const gchar *model, G_GNUC_UNUSED const gchar *fw, G_GNUC_UNUSED gboolean include_defaults) {
45 : return NULL;
46 : }
47 : #else
48 :
49 : struct drive_settings {
50 : const char* modelfamily;
51 : const char* modelregexp;
52 : const char* firmwareregexp;
53 : const char* warningmsg;
54 : const char* presets;
55 : };
56 :
57 : static const struct drive_settings builtin_knowndrives[] = {
58 : #include <drivedb.h>
59 : };
60 :
61 :
62 129 : static gboolean parse_attribute_def (const char *arg, gint *attr_id, gchar **attr_name) {
63 129 : int format_str_len = 0;
64 129 : int attrname_str_len = 0;
65 129 : int hddssd_str_len = 0;
66 129 : char format_str[33] = {0,};
67 129 : char attrname_str[33] = {0,};
68 129 : char hddssd_str[4] = {0,};
69 :
70 129 : if (arg[0] == 'N')
71 : /* ignoring "N,format[,name]" as it doesn't provide attribute ID */
72 0 : return FALSE;
73 :
74 : /* parse "id,format[+][,name[,HDD|SSD]]" */
75 129 : if (sscanf (arg, "%d,%32[^,]%n,%32[^,]%n,%3[DHS]%n",
76 : attr_id,
77 : format_str, &format_str_len,
78 : attrname_str, &attrname_str_len,
79 : hddssd_str, &hddssd_str_len) < 2)
80 0 : return FALSE;
81 129 : if (*attr_id < 1 || *attr_id > 255)
82 0 : return FALSE;
83 129 : if (attrname_str_len < 1)
84 1 : return FALSE;
85 :
86 : /* ignoring format_str */
87 128 : *attr_name = g_strndup (attrname_str, attrname_str_len);
88 : /* ignoring hddssd_str */
89 :
90 128 : return TRUE;
91 : }
92 :
93 5 : static void parse_presets_str (const char *presets, GHashTable *attrs) {
94 129 : while (TRUE) {
95 : char opt;
96 134 : char arg[94] = {0,};
97 134 : int len = 0;
98 134 : gint attr_id = 0;
99 134 : gchar *attr_name = NULL;
100 :
101 134 : presets += strspn (presets, " \t");
102 134 : if (presets[0] == '\0')
103 5 : break;
104 129 : if (sscanf (presets, "-%c %80[^ ]%n", &opt, arg, &len) < 2)
105 0 : break;
106 129 : if (len < 1)
107 0 : break;
108 129 : if (opt == 'v') {
109 : /* parse "-v N,format[,name[,HDD|SSD]]" */
110 129 : if (parse_attribute_def (arg, &attr_id, &attr_name)) {
111 128 : g_hash_table_replace (attrs, GINT_TO_POINTER (attr_id), attr_name);
112 : }
113 : }
114 : /* ignoring other switches like 'F' and 'd' */
115 :
116 129 : presets += len;
117 : }
118 5 : }
119 :
120 13 : DriveDBAttr** drivedb_lookup_drive (const gchar *model, const gchar *fw, gboolean include_defaults) {
121 : gulong i;
122 : GHashTable *attrs;
123 13 : DriveDBAttr **ret = NULL;
124 :
125 : if (G_N_ELEMENTS (builtin_knowndrives) == 0)
126 : return NULL;
127 :
128 13 : attrs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
129 :
130 : /* first parse the DEFAULTS definitions */
131 13 : if (include_defaults)
132 0 : for (i = 0; i < G_N_ELEMENTS (builtin_knowndrives); i++)
133 0 : if (builtin_knowndrives[i].modelfamily &&
134 0 : builtin_knowndrives[i].presets &&
135 0 : g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "DEFAULT", 7) == 0)
136 0 : parse_presets_str (builtin_knowndrives[i].presets, attrs);
137 :
138 : /* now overlay/replace with drive specific keys */
139 10426 : for (i = 0; i < G_N_ELEMENTS (builtin_knowndrives); i++) {
140 : GRegex *regex;
141 10413 : GError *error = NULL;
142 10413 : gboolean match = FALSE;
143 :
144 : /* check the modelfamily string */
145 10413 : if (builtin_knowndrives[i].modelfamily == NULL ||
146 10413 : builtin_knowndrives[i].modelregexp == NULL ||
147 16042 : builtin_knowndrives[i].presets == NULL || strlen (builtin_knowndrives[i].presets) < 5 ||
148 11258 : g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "VERSION", 7) == 0 ||
149 8450 : g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "USB", 3) == 0 ||
150 2821 : g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "DEFAULT", 7) == 0)
151 10407 : continue;
152 : /* assuming modelfamily=ATA from now on... */
153 :
154 : /* match the model string */
155 2808 : regex = g_regex_new (builtin_knowndrives[i].modelregexp,
156 : 0, 0, &error);
157 2808 : if (regex == NULL) {
158 0 : bd_utils_log_format (BD_UTILS_LOG_DEBUG,
159 : "drivedb-parser: regex compilation failed for '%s': %s",
160 0 : builtin_knowndrives[i].modelregexp,
161 0 : error->message);
162 0 : g_error_free (error);
163 0 : continue;
164 : }
165 2808 : if (g_regex_match (regex, model, 0, NULL))
166 6 : match = TRUE;
167 2808 : g_regex_unref (regex);
168 2808 : if (!match)
169 2802 : continue;
170 :
171 : /* match the firmware string */
172 6 : if (builtin_knowndrives[i].firmwareregexp &&
173 6 : strlen (builtin_knowndrives[i].firmwareregexp) > 0 &&
174 1 : fw && strlen (fw) > 0)
175 : {
176 1 : regex = g_regex_new (builtin_knowndrives[i].firmwareregexp,
177 : 0, 0, &error);
178 1 : if (regex == NULL) {
179 0 : bd_utils_log_format (BD_UTILS_LOG_DEBUG,
180 : "drivedb-parser: regex compilation failed for '%s': %s",
181 0 : builtin_knowndrives[i].firmwareregexp,
182 0 : error->message);
183 0 : g_error_free (error);
184 0 : continue;
185 : }
186 1 : if (!g_regex_match (regex, model, 0, NULL))
187 1 : match = FALSE;
188 1 : g_regex_unref (regex);
189 : }
190 :
191 6 : if (match)
192 5 : parse_presets_str (builtin_knowndrives[i].presets, attrs);
193 : }
194 :
195 13 : if (g_hash_table_size (attrs) > 0) {
196 : GHashTableIter iter;
197 : gpointer key, val;
198 :
199 : /* convert to NULL-terminated array */
200 4 : i = 0;
201 4 : ret = g_new0 (DriveDBAttr *, g_hash_table_size (attrs) + 1);
202 4 : g_hash_table_iter_init (&iter, attrs);
203 132 : while (g_hash_table_iter_next (&iter, &key, &val)) {
204 : DriveDBAttr *attr;
205 :
206 128 : attr = g_new0 (DriveDBAttr, 1);
207 128 : attr->id = GPOINTER_TO_INT (key);
208 128 : attr->name = g_strdup (val);
209 128 : ret[i++] = attr;
210 : }
211 : }
212 13 : g_hash_table_destroy (attrs);
213 :
214 13 : return ret;
215 : }
216 : #endif /* HAVE_DRIVEDB_H */
|