VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/sysfs.cpp@ 23309

Last change on this file since 23309 was 23309, checked in by vboxsync, 16 years ago

iprt/linux: add APIs for locating device nodes to sysfs APIs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.3 KB
Line 
1/* $Id: sysfs.cpp 23309 2009-09-24 19:44:27Z vboxsync $ */
2/** @file
3 * IPRT - Linux sysfs access.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_SYSTEM
36#include <iprt/linux/sysfs.h>
37#include <iprt/assert.h>
38#include <iprt/dir.h>
39#include <iprt/err.h>
40#include <iprt/fs.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/string.h>
44
45#include <unistd.h>
46#include <stdio.h>
47#include <sys/sysctl.h>
48#include <sys/stat.h>
49#include <sys/fcntl.h>
50#include <errno.h>
51
52
53/**
54 * Constructs the path of a sysfs file from the format paramaters passed,
55 * prepending a prefix if the path is relative.
56 *
57 * @returns The number of characters returned, or -1 and errno set to ERANGE on
58 * failure.
59 *
60 * @param pszPrefix The prefix to prepend if the path is relative. Must end
61 * in '/'.
62 * @param pszBuf Where to write the path. Must be at least
63 * sizeof(@a pszPrefix) characters long
64 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
65 * @param pszFormat The name format, either absolute or relative to "/sys/".
66 * @param va The format args.
67 */
68static ssize_t rtLinuxConstructPathV(char *pszBuf, size_t cchBuf,
69 const char *pszPrefix,
70 const char *pszFormat, va_list va)
71{
72 size_t cchPrefix = strlen(pszPrefix);
73 AssertReturnStmt(pszPrefix[cchPrefix - 1] == '/', errno = ERANGE, -1);
74 AssertReturnStmt(cchBuf > cchPrefix + 1, errno = ERANGE, -1);
75
76 size_t cch = RTStrPrintfV(pszBuf, cchBuf, pszFormat, va);
77 if (*pszBuf != '/')
78 {
79 AssertReturnStmt(cchBuf >= cch + cchPrefix + 1, errno = ERANGE, -1);
80 memmove(pszBuf + cchPrefix, pszBuf, cch + 1);
81 memcpy(pszBuf, pszPrefix, cchPrefix);
82 cch += cchPrefix;
83 }
84 return cch;
85}
86
87
88/**
89 * Constructs the path of a sysfs file from the format paramaters passed,
90 * prepending a prefix if the path is relative.
91 *
92 * @returns The number of characters returned, or -1 and errno set to ERANGE on
93 * failure.
94 *
95 * @param pszPrefix The prefix to prepend if the path is relative. Must end
96 * in '/'.
97 * @param pszBuf Where to write the path. Must be at least
98 * sizeof(@a pszPrefix) characters long
99 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
100 * @param pszFormat The name format, either absolute or relative to "/sys/".
101 * @param ... The format args.
102 */
103static ssize_t rtLinuxConstructPath(char *pszBuf, size_t cchBuf,
104 const char *pszPrefix,
105 const char *pszFormat, ...)
106{
107 va_list va;
108 va_start(va, pszFormat);
109 int rc = rtLinuxConstructPathV(pszBuf, cchBuf, pszPrefix, pszFormat, va);
110 va_end(va);
111 return rc;
112}
113
114
115/**
116 * Constructs the path of a sysfs file from the format paramaters passed,
117 * prepending "/sys/" if the path is relative.
118 *
119 * @returns The number of characters returned, or -1 and errno set to ERANGE on
120 * failure.
121 *
122 * @param pszBuf Where to write the path. Must be at least
123 * sizeof("/sys/") characters long
124 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
125 * @param pszFormat The name format, either absolute or relative to "/sys/".
126 * @param va The format args.
127 */
128static ssize_t rtLinuxSysFsConstructPath(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
129{
130 return rtLinuxConstructPathV(pszBuf, cchBuf, "/sys/", pszFormat, va);
131}
132
133
134RTDECL(bool) RTLinuxSysFsExistsV(const char *pszFormat, va_list va)
135{
136 int iSavedErrno = errno;
137
138 /*
139 * Construct the filename and call stat.
140 */
141 bool fRet = false;
142 char szFilename[RTPATH_MAX];
143 ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
144 if (rc != -1)
145 {
146 struct stat st;
147 fRet = stat(szFilename, &st) == 0;
148 }
149
150 errno = iSavedErrno;
151 return fRet;
152}
153
154
155RTDECL(bool) RTLinuxSysFsExists(const char *pszFormat, ...)
156{
157 va_list va;
158 va_start(va, pszFormat);
159 bool fRet = RTLinuxSysFsExistsV(pszFormat, va);
160 va_end(va);
161 return fRet;
162}
163
164
165RTDECL(int) RTLinuxSysFsOpenV(const char *pszFormat, va_list va)
166{
167 /*
168 * Construct the filename and call open.
169 */
170 char szFilename[RTPATH_MAX];
171 ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
172 if (rc != -1)
173 rc = open(szFilename, O_RDONLY, 0);
174 return rc;
175}
176
177
178RTDECL(int) RTLinuxSysFsOpen(const char *pszFormat, ...)
179{
180 va_list va;
181 va_start(va, pszFormat);
182 int fd = RTLinuxSysFsOpenV(pszFormat, va);
183 va_end(va);
184 return fd;
185}
186
187
188RTDECL(void) RTLinuxSysFsClose(int fd)
189{
190 int iSavedErrno = errno;
191 close(fd);
192 errno = iSavedErrno;
193}
194
195
196RTDECL(ssize_t) RTLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
197{
198 Assert(cchBuf > 1);
199 ssize_t cchRead = read(fd, pszBuf, cchBuf - 1);
200 pszBuf[cchRead >= 0 ? cchRead : 0] = '\0';
201 return cchRead;
202}
203
204
205RTDECL(int64_t) RTLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
206{
207 int fd = RTLinuxSysFsOpenV(pszFormat, va);
208 if (fd == -1)
209 return -1;
210
211 int64_t i64Ret = -1;
212 char szNum[128];
213 ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
214 if (cchNum > 0)
215 {
216 int rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret);
217 if (RT_FAILURE(rc))
218 {
219 i64Ret = -1;
220 errno = -ETXTBSY; /* just something that won't happen at read / open. */
221 }
222 }
223 else if (cchNum == 0)
224 errno = -ETXTBSY; /* just something that won't happen at read / open. */
225
226 RTLinuxSysFsClose(fd);
227 return i64Ret;
228}
229
230
231RTDECL(int64_t) RTLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...)
232{
233 va_list va;
234 va_start(va, pszFormat);
235 int64_t i64Ret = RTLinuxSysFsReadIntFileV(uBase, pszFormat, va);
236 va_end(va);
237 return i64Ret;
238}
239
240
241RTDECL(dev_t) RTLinuxSysFsReadDevNumFileV(const char *pszFormat, va_list va)
242{
243 int fd = RTLinuxSysFsOpenV(pszFormat, va);
244 if (fd == -1)
245 return 0;
246
247 dev_t devNum = 0;
248 char szNum[128];
249 ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
250 if (cchNum > 0)
251 {
252 uint32_t u32Maj = 0;
253 uint32_t u32Min = 0;
254 char *pszNext = NULL;
255 int rc = RTStrToUInt32Ex(szNum, &pszNext, 10, &u32Maj);
256 if (RT_FAILURE(rc) || (rc != VWRN_TRAILING_CHARS) || (*pszNext != ':'))
257 errno = EINVAL;
258 else
259 {
260 rc = RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32Min);
261 if ( rc != VINF_SUCCESS
262 && rc != VWRN_TRAILING_CHARS
263 && rc != VWRN_TRAILING_SPACES)
264 errno = EINVAL;
265 else
266 {
267 errno = 0;
268 devNum = makedev(u32Maj, u32Min);
269 }
270 }
271 }
272 else if (cchNum == 0)
273 errno = EINVAL;
274
275 RTLinuxSysFsClose(fd);
276 return devNum;
277}
278
279
280RTDECL(dev_t) RTLinuxSysFsReadDevNumFile(const char *pszFormat, ...)
281{
282 va_list va;
283 va_start(va, pszFormat);
284 dev_t devNum = RTLinuxSysFsReadDevNumFileV(pszFormat, va);
285 va_end(va);
286 return devNum;
287}
288
289
290RTDECL(ssize_t) RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
291{
292 int fd = RTLinuxSysFsOpenV(pszFormat, va);
293 if (fd == -1)
294 return -1;
295
296 ssize_t cchRet = RTLinuxSysFsReadStr(fd, pszBuf, cchBuf);
297 RTLinuxSysFsClose(fd);
298 if (cchRet > 0)
299 {
300 char *pchNewLine = (char *)memchr(pszBuf, '\n', cchRet);
301 if (pchNewLine)
302 *pchNewLine = '\0';
303 }
304 return cchRet;
305}
306
307
308RTDECL(ssize_t) RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
309{
310 va_list va;
311 va_start(va, pszFormat);
312 ssize_t cchRet = RTLinuxSysFsReadStrFileV(pszBuf, cchBuf, pszFormat, va);
313 va_end(va);
314 return cchRet;
315}
316
317
318RTDECL(ssize_t) RTLinuxSysFsGetLinkDestV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
319{
320 AssertReturnStmt(cchBuf >= 2, errno = EINVAL, -1);
321
322 /*
323 * Construct the filename and read the link.
324 */
325 char szFilename[RTPATH_MAX];
326 int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
327 if (rc == -1)
328 return -1;
329
330 char szLink[RTPATH_MAX];
331 rc = readlink(szFilename, szLink, sizeof(szLink));
332 if (rc == -1)
333 return -1;
334 if ((size_t)rc > sizeof(szLink) - 1)
335 {
336 errno = ERANGE;
337 return -1;
338 }
339 szLink[rc] = '\0'; /* readlink fun. */
340
341 /*
342 * Extract the file name component and copy it into the return buffer.
343 */
344 size_t cchName;
345 const char *pszName = RTPathFilename(szLink);
346 if (pszName)
347 {
348 cchName = strlen(pszName);
349 if (cchName >= cchBuf)
350 {
351 errno = ERANGE;
352 return -1;
353 }
354 memcpy(pszBuf, pszName, cchName);
355 }
356 else
357 {
358 *pszBuf = '\0';
359 cchName = 0;
360 }
361 return cchName;
362}
363
364
365RTDECL(ssize_t) RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
366{
367 va_list va;
368 va_start(va, pszFormat);
369 int rc = RTLinuxSysFsGetLinkDestV(pszBuf, cchBuf, pszFormat, va);
370 va_end(va);
371 return rc;
372}
373
374
375static ssize_t rtLinuxFindDevicePathRecursive(dev_t devNum, RTFMODE fMode,
376 const char *pszBasePath,
377 char *pszBuf, size_t cchBuf)
378{
379 PRTDIR pDir = NULL;
380 RTDIRENTRYEX entry = { {0} };
381 int vrc = VINF_SUCCESS;
382 ssize_t rc;
383
384 vrc = RTDirOpen(&pDir, pszBasePath);
385 if (RT_SUCCESS(vrc))
386 while (true)
387 {
388 vrc = RTDirReadEx(pDir, &entry, NULL, RTFSOBJATTRADD_UNIX);
389 if (RT_FAILURE(vrc))
390 {
391 errno = (vrc == VERR_NO_MORE_FILES) ? ENOENT
392 : (vrc == VERR_BUFFER_OVERFLOW) ? EOVERFLOW
393 : EIO;
394 rc = -1;
395 break;
396 }
397 if (RTFS_IS_SYMLINK(entry.Info.Attr.fMode))
398 continue;
399 if ( (entry.Info.Attr.u.Unix.Device == devNum)
400 && ( ( (fMode == RTFS_TYPE_DEV_CHAR)
401 && RTFS_IS_DEV_CHAR(entry.Info.Attr.fMode))
402 || ( (fMode == RTFS_TYPE_DEV_BLOCK)
403 && RTFS_IS_DEV_BLOCK(entry.Info.Attr.fMode))))
404 {
405 rc = rtLinuxConstructPath(pszBuf, cchBuf, pszBasePath, "%s",
406 entry.szName);
407 break;
408 }
409 if (!RTFS_IS_DIRECTORY(entry.Info.Attr.fMode))
410 continue;
411 if (entry.szName[0] == '.')
412 continue;
413 char szPath[RTPATH_MAX];
414 /** @todo this is a temporary hack, as RTDirReadEx currently
415 * doesn't know about symbolic links */
416 rc = rtLinuxConstructPath(szPath, sizeof(szPath), pszBasePath,
417 "%s", entry.szName);
418 if (rc < 0)
419 break;
420 struct stat Stat = { 0 };
421 if ( lstat(szPath, &Stat) < 0
422 || S_ISLNK(Stat.st_mode))
423 continue;
424 /* @todo ends here */
425 rc = rtLinuxConstructPath(szPath, sizeof(szPath), pszBasePath,
426 "%s/", entry.szName);
427 if (rc < 0)
428 break;
429 rc = rtLinuxFindDevicePathRecursive(devNum, fMode, szPath,
430 pszBuf, cchBuf);
431 if ((rc >= 0) || errno != ENOENT)
432 break;
433 }
434 else
435 {
436 rc = -1;
437 errno = RTErrConvertToErrno(vrc);
438 }
439 RTDirClose(pDir);
440 return rc;
441}
442
443
444RTDECL(ssize_t) RTLinuxFindDevicePathV(dev_t devNum, RTFMODE fMode,
445 char *pszBuf, size_t cchBuf,
446 const char *pszSuggestion, va_list va)
447{
448 AssertReturnStmt(cchBuf >= 2, errno = EINVAL, -1);
449 AssertReturnStmt( (fMode == RTFS_TYPE_DEV_CHAR)
450 || (fMode == RTFS_TYPE_DEV_BLOCK),
451 errno = EINVAL, -1);
452
453 if (!pszSuggestion)
454 return rtLinuxFindDevicePathRecursive(devNum, fMode, "/dev/",
455 pszBuf, cchBuf);
456
457 /*
458 * Construct the filename and read the link.
459 */
460 char szFilename[RTPATH_MAX];
461 int rc = rtLinuxConstructPathV(szFilename, sizeof(szFilename), "/dev/",
462 pszSuggestion, va);
463 if (rc == -1)
464 return -1;
465
466 /*
467 * Check whether the caller's suggestion was right.
468 */
469 /** @todo Should we just be using POSIX stat here? */
470 RTFSOBJINFO info = {0};
471 int vrc = RTPathQueryInfo(szFilename, &info, RTFSOBJATTRADD_UNIX);
472 if (RT_FAILURE(vrc))
473 {
474 errno = (vrc == VERR_PATH_NOT_FOUND) ? ENOENT
475 : (vrc == VERR_FILE_NOT_FOUND) ? ENOENT
476 : EIO;
477 return -1;
478 }
479 if ( (info.Attr.u.Unix.Device == devNum)
480 && ( ( (fMode == RTFS_TYPE_DEV_CHAR)
481 && RTFS_IS_DEV_CHAR(info.Attr.fMode))
482 || ( (fMode == RTFS_TYPE_DEV_BLOCK)
483 && RTFS_IS_DEV_BLOCK(info.Attr.fMode))))
484 {
485 size_t cchPath = strlen(szFilename) + 1;
486 if (cchPath > cchBuf)
487 {
488 errno = EOVERFLOW;
489 return -1;
490 }
491 strcpy(pszBuf, szFilename);
492 return cchPath;
493 }
494
495 /* If the suggestion was wrong, try the brute force method */
496 return rtLinuxFindDevicePathRecursive(devNum, fMode, "/dev/",
497 pszBuf, cchBuf);
498}
499
500
501RTDECL(ssize_t) RTLinuxFindDevicePath(dev_t devNum, RTFMODE fMode,
502 char *pszBuf, size_t cchBuf,
503 const char *pszSuggestion, ...)
504{
505 va_list va;
506 va_start(va, pszSuggestion);
507 int rc = RTLinuxFindDevicePathV(devNum, fMode, pszBuf, cchBuf,
508 pszSuggestion, va);
509 va_end(va);
510 return rc;
511}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette