Line data Source code
1 : /*
2 : * Copyright (C) 2017 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: Vratislav Podzimek <vpodzime@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <libudev.h>
22 :
23 : #include "dev_utils.h"
24 :
25 : /**
26 : * bd_utils_dev_utils_error_quark: (skip)
27 : */
28 0 : GQuark bd_utils_dev_utils_error_quark (void)
29 : {
30 0 : return g_quark_from_static_string ("g-bd-utils-dev_utils-error-quark");
31 : }
32 :
33 : /**
34 : * bd_utils_resolve_device:
35 : * @dev_spec: specification of the device (e.g. "/dev/sda", any symlink, or the name of a file
36 : * under "/dev")
37 : * @error: (out) (optional): place to store error (if any)
38 : *
39 : * Returns: (transfer full): the full real path of the device (e.g. "/dev/md126"
40 : * for "/dev/md/my_raid") or %NULL in case of error
41 : */
42 55 : gchar* bd_utils_resolve_device (const gchar *dev_spec, GError **error) {
43 55 : gchar *path = NULL;
44 55 : gchar *symlink = NULL;
45 55 : GError *l_error = NULL;
46 :
47 : /* TODO: check that the resulting path is a block device? */
48 :
49 55 : if (!g_str_has_prefix (dev_spec, "/dev/"))
50 6 : path = g_strdup_printf ("/dev/%s", dev_spec);
51 : else
52 49 : path = g_strdup (dev_spec);
53 :
54 55 : symlink = g_file_read_link (path, &l_error);
55 55 : if (!symlink) {
56 25 : if (g_error_matches (l_error, G_FILE_ERROR, G_FILE_ERROR_INVAL)) {
57 : /* invalid argument -> not a symlink -> nothing to resolve */
58 6 : g_clear_error (&l_error);
59 6 : return path;
60 : } else {
61 : /* some other error, just report it */
62 19 : g_propagate_error (error, l_error);
63 19 : g_free (path);
64 19 : return NULL;
65 : }
66 : }
67 30 : g_free (path);
68 :
69 30 : if (g_str_has_prefix (symlink, "../"))
70 30 : path = g_strdup_printf ("/dev/%s", symlink + 3);
71 : else
72 0 : path = g_strdup_printf ("/dev/%s", symlink);
73 30 : g_free (symlink);
74 :
75 30 : return path;
76 : }
77 :
78 : /**
79 : * bd_utils_get_device_symlinks:
80 : * @dev_spec: specification of the device (e.g. "/dev/sda", any symlink, or the name of a file
81 : * under "/dev")
82 : * @error: (out) (optional): place to store error (if any)
83 : *
84 : * Returns: (transfer full) (array zero-terminated=1): a list of all symlinks (known to udev) for the
85 : * device specified with @dev_spec or %NULL in
86 : * case of error
87 : */
88 4 : gchar** bd_utils_get_device_symlinks (const gchar *dev_spec, GError **error) {
89 : gchar *dev_path;
90 : struct udev *context;
91 : struct udev_device *device;
92 4 : struct udev_list_entry *entry = NULL;
93 4 : struct udev_list_entry *ent_it = NULL;
94 4 : guint64 n_links = 0;
95 4 : guint64 i = 0;
96 4 : gchar **ret = NULL;
97 :
98 4 : dev_path = bd_utils_resolve_device (dev_spec, error);
99 4 : if (!dev_path)
100 1 : return NULL;
101 :
102 3 : context = udev_new ();
103 : /* dev_path is the full path like "/dev/sda", we only need the device name ("sda") */
104 3 : device = udev_device_new_from_subsystem_sysname (context, "block", dev_path + 5);
105 :
106 3 : if (!device) {
107 0 : g_set_error (error, BD_UTILS_DEV_UTILS_ERROR, BD_UTILS_DEV_UTILS_ERROR_FAILED,
108 : "Failed to get information about the device '%s' from udev database",
109 : dev_path);
110 0 : g_free (dev_path);
111 0 : udev_unref (context);
112 0 : return NULL;
113 : }
114 :
115 3 : entry = udev_device_get_devlinks_list_entry (device);
116 3 : if (!entry) {
117 0 : g_set_error (error, BD_UTILS_DEV_UTILS_ERROR, BD_UTILS_DEV_UTILS_ERROR_FAILED,
118 : "Failed to get symlinks for the device '%s'", dev_path);
119 0 : g_free (dev_path);
120 0 : udev_device_unref (device);
121 0 : udev_unref (context);
122 0 : return NULL;
123 : }
124 3 : g_free (dev_path);
125 :
126 3 : ent_it = entry;
127 14 : while (ent_it) {
128 11 : n_links++;
129 11 : ent_it = udev_list_entry_get_next (ent_it);
130 : }
131 :
132 3 : ret = g_new0 (gchar*, n_links + 1);
133 3 : ent_it = entry;
134 14 : while (ent_it) {
135 11 : ret[i++] = g_strdup (udev_list_entry_get_name (ent_it));
136 11 : ent_it = udev_list_entry_get_next (ent_it);
137 : }
138 3 : ret[i] = NULL;
139 :
140 3 : udev_device_unref (device);
141 3 : udev_unref (context);
142 :
143 3 : return ret;
144 : }
|