VirtualBox

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

Last change on this file since 26977 was 26608, checked in by vboxsync, 15 years ago

IPRT: linux implementation of RTSystemQueryDmiString.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.9 KB
Line 
1/* $Id: sysfs.cpp 26608 2010-02-17 12:48:33Z 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.virtualbox.org. 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/** @todo r=bird: This whole API should be rewritten to use IPRT status codes.
53 * using errno was a mistake and only results in horrible code. */
54
55
56/**
57 * Constructs the path of a sysfs file from the format paramaters passed,
58 * prepending a prefix if the path is relative.
59 *
60 * @returns The number of characters returned, or -1 and errno set to ERANGE on
61 * failure.
62 *
63 * @param pszPrefix The prefix to prepend if the path is relative. Must end
64 * in '/'.
65 * @param pszBuf Where to write the path. Must be at least
66 * sizeof(@a pszPrefix) characters long
67 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
68 * @param pszFormat The name format, either absolute or relative to the
69 * prefix specified by @a pszPrefix.
70 * @param va The format args.
71 */
72static ssize_t rtLinuxConstructPathV(char *pszBuf, size_t cchBuf,
73 const char *pszPrefix,
74 const char *pszFormat, va_list va)
75{
76 size_t cchPrefix = strlen(pszPrefix);
77 AssertReturnStmt(pszPrefix[cchPrefix - 1] == '/', errno = ERANGE, -1);
78 AssertReturnStmt(cchBuf > cchPrefix + 1, errno = ERANGE, -1);
79
80 /** @todo While RTStrPrintfV prevents overflows, it doesn't make it easy to
81 * check for truncations. RTPath should provide some formatters and
82 * joiners which can take over this rather common task that is
83 * performed here. */
84 size_t cch = RTStrPrintfV(pszBuf, cchBuf, pszFormat, va);
85 if (*pszBuf != '/')
86 {
87 AssertReturnStmt(cchBuf >= cch + cchPrefix + 1, errno = ERANGE, -1);
88 memmove(pszBuf + cchPrefix, pszBuf, cch + 1);
89 memcpy(pszBuf, pszPrefix, cchPrefix);
90 cch += cchPrefix;
91 }
92 return cch;
93}
94
95
96/**
97 * Constructs the path of a sysfs file from the format paramaters passed,
98 * prepending a prefix if the path is relative.
99 *
100 * @returns The number of characters returned, or -1 and errno set to ERANGE on
101 * failure.
102 *
103 * @param pszPrefix The prefix to prepend if the path is relative. Must end
104 * in '/'.
105 * @param pszBuf Where to write the path. Must be at least
106 * sizeof(@a pszPrefix) characters long
107 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
108 * @param pszFormat The name format, either absolute or relative to "/sys/".
109 * @param ... The format args.
110 */
111static ssize_t rtLinuxConstructPath(char *pszBuf, size_t cchBuf,
112 const char *pszPrefix,
113 const char *pszFormat, ...)
114{
115 va_list va;
116 va_start(va, pszFormat);
117 int rc = rtLinuxConstructPathV(pszBuf, cchBuf, pszPrefix, pszFormat, va);
118 va_end(va);
119 return rc;
120}
121
122
123/**
124 * Constructs the path of a sysfs file from the format paramaters passed,
125 * prepending "/sys/" if the path is relative.
126 *
127 * @returns The number of characters returned, or -1 and errno set to ERANGE on
128 * failure.
129 *
130 * @param pszBuf Where to write the path. Must be at least
131 * sizeof("/sys/") characters long
132 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
133 * @param pszFormat The name format, either absolute or relative to "/sys/".
134 * @param va The format args.
135 */
136static ssize_t rtLinuxSysFsConstructPath(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
137{
138 return rtLinuxConstructPathV(pszBuf, cchBuf, "/sys/", pszFormat, va);
139}
140
141
142RTDECL(bool) RTLinuxSysFsExistsV(const char *pszFormat, va_list va)
143{
144 int iSavedErrno = errno;
145
146 /*
147 * Construct the filename and call stat.
148 */
149 bool fRet = false;
150 char szFilename[RTPATH_MAX];
151 ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
152 if (rc != -1)
153 {
154 struct stat st;
155 fRet = stat(szFilename, &st) == 0;
156 }
157
158 errno = iSavedErrno;
159 return fRet;
160}
161
162
163RTDECL(bool) RTLinuxSysFsExists(const char *pszFormat, ...)
164{
165 va_list va;
166 va_start(va, pszFormat);
167 bool fRet = RTLinuxSysFsExistsV(pszFormat, va);
168 va_end(va);
169 return fRet;
170}
171
172
173RTDECL(int) RTLinuxSysFsOpenV(const char *pszFormat, va_list va)
174{
175 /*
176 * Construct the filename and call open.
177 */
178 char szFilename[RTPATH_MAX];
179 ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
180 if (rc != -1)
181 rc = open(szFilename, O_RDONLY, 0);
182 return rc;
183}
184
185
186RTDECL(int) RTLinuxSysFsOpen(const char *pszFormat, ...)
187{
188 va_list va;
189 va_start(va, pszFormat);
190 int fd = RTLinuxSysFsOpenV(pszFormat, va);
191 va_end(va);
192 return fd;
193}
194
195
196RTDECL(void) RTLinuxSysFsClose(int fd)
197{
198 int iSavedErrno = errno;
199 close(fd);
200 errno = iSavedErrno;
201}
202
203
204RTDECL(ssize_t) RTLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
205{
206 Assert(cchBuf > 1);
207 ssize_t cchRead = read(fd, pszBuf, cchBuf - 1);
208 pszBuf[cchRead >= 0 ? cchRead : 0] = '\0';
209 return cchRead;
210}
211
212
213RTDECL(int) RTLinuxSysFsReadFile(int fd, void *pvBuf, size_t cbBuf, size_t *pcbRead)
214{
215 int rc;
216 ssize_t cbRead = read(fd, pvBuf, cbBuf);
217 if (cbRead >= 0)
218 {
219 if (pcbRead)
220 *pcbRead = cbRead;
221 if ((size_t)cbRead < cbBuf)
222 rc = VINF_SUCCESS;
223 else
224 {
225 /* Check for EOF */
226 char ch;
227 off_t off = lseek(fd, 0, SEEK_CUR);
228 ssize_t cbRead2 = read(fd, &ch, 1);
229 if (cbRead2 == 0)
230 rc = VINF_SUCCESS;
231 else if (cbRead2 > 0)
232 {
233 lseek(fd, off, SEEK_SET);
234 rc = VERR_BUFFER_OVERFLOW;
235 }
236 else
237 rc = RTErrConvertFromErrno(errno);
238 }
239 }
240 else
241 rc = RTErrConvertFromErrno(errno);
242 return rc;
243}
244
245
246RTDECL(int64_t) RTLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
247{
248 int fd = RTLinuxSysFsOpenV(pszFormat, va);
249 if (fd == -1)
250 return -1;
251
252 int64_t i64Ret = -1;
253 char szNum[128];
254 ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
255 if (cchNum > 0)
256 {
257 int rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret);
258 if (RT_FAILURE(rc))
259 {
260 i64Ret = -1;
261 errno = -ETXTBSY; /* just something that won't happen at read / open. */
262 }
263 }
264 else if (cchNum == 0)
265 errno = -ETXTBSY; /* just something that won't happen at read / open. */
266
267 RTLinuxSysFsClose(fd);
268 return i64Ret;
269}
270
271
272RTDECL(int64_t) RTLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...)
273{
274 va_list va;
275 va_start(va, pszFormat);
276 int64_t i64Ret = RTLinuxSysFsReadIntFileV(uBase, pszFormat, va);
277 va_end(va);
278 return i64Ret;
279}
280
281
282RTDECL(dev_t) RTLinuxSysFsReadDevNumFileV(const char *pszFormat, va_list va)
283{
284 int fd = RTLinuxSysFsOpenV(pszFormat, va);
285 if (fd == -1)
286 return 0;
287
288 dev_t DevNum = 0;
289 char szNum[128];
290 ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
291 if (cchNum > 0)
292 {
293 uint32_t u32Maj = 0;
294 uint32_t u32Min = 0;
295 char *pszNext = NULL;
296 int rc = RTStrToUInt32Ex(szNum, &pszNext, 10, &u32Maj);
297 if (RT_FAILURE(rc) || (rc != VWRN_TRAILING_CHARS) || (*pszNext != ':'))
298 errno = EINVAL;
299 else
300 {
301 rc = RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32Min);
302 if ( rc != VINF_SUCCESS
303 && rc != VWRN_TRAILING_CHARS
304 && rc != VWRN_TRAILING_SPACES)
305 errno = EINVAL;
306 else
307 {
308 errno = 0;
309 DevNum = makedev(u32Maj, u32Min);
310 }
311 }
312 }
313 else if (cchNum == 0)
314 errno = EINVAL;
315
316 RTLinuxSysFsClose(fd);
317 return DevNum;
318}
319
320
321RTDECL(dev_t) RTLinuxSysFsReadDevNumFile(const char *pszFormat, ...)
322{
323 va_list va;
324 va_start(va, pszFormat);
325 dev_t DevNum = RTLinuxSysFsReadDevNumFileV(pszFormat, va);
326 va_end(va);
327 return DevNum;
328}
329
330
331RTDECL(ssize_t) RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
332{
333 int fd = RTLinuxSysFsOpenV(pszFormat, va);
334 if (fd == -1)
335 return -1;
336
337 ssize_t cchRet = RTLinuxSysFsReadStr(fd, pszBuf, cchBuf);
338 RTLinuxSysFsClose(fd);
339 if (cchRet > 0)
340 {
341 char *pchNewLine = (char *)memchr(pszBuf, '\n', cchRet);
342 if (pchNewLine)
343 *pchNewLine = '\0';
344 }
345 return cchRet;
346}
347
348
349RTDECL(ssize_t) RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
350{
351 va_list va;
352 va_start(va, pszFormat);
353 ssize_t cchRet = RTLinuxSysFsReadStrFileV(pszBuf, cchBuf, pszFormat, va);
354 va_end(va);
355 return cchRet;
356}
357
358
359RTDECL(ssize_t) RTLinuxSysFsGetLinkDestV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
360{
361 AssertReturnStmt(cchBuf >= 2, errno = EINVAL, -1);
362
363 /*
364 * Construct the filename and read the link.
365 */
366 char szFilename[RTPATH_MAX];
367 int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
368 if (rc == -1)
369 return -1;
370
371 char szLink[RTPATH_MAX];
372 rc = readlink(szFilename, szLink, sizeof(szLink));
373 if (rc == -1)
374 return -1;
375 if ((size_t)rc > sizeof(szLink) - 1)
376 {
377 errno = ERANGE;
378 return -1;
379 }
380 szLink[rc] = '\0'; /* readlink fun. */
381
382 /*
383 * Extract the file name component and copy it into the return buffer.
384 */
385 size_t cchName;
386 const char *pszName = RTPathFilename(szLink);
387 if (pszName)
388 {
389 cchName = strlen(pszName); /* = &szLink[rc] - pszName; */
390 if (cchName >= cchBuf)
391 {
392 errno = ERANGE;
393 return -1;
394 }
395 memcpy(pszBuf, pszName, cchName + 1);
396 }
397 else
398 {
399 *pszBuf = '\0';
400 cchName = 0;
401 }
402 return cchName;
403}
404
405
406RTDECL(ssize_t) RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
407{
408 va_list va;
409 va_start(va, pszFormat);
410 int rc = RTLinuxSysFsGetLinkDestV(pszBuf, cchBuf, pszFormat, va);
411 va_end(va);
412 return rc;
413}
414
415
416static ssize_t rtLinuxFindDevicePathRecursive(dev_t DevNum, RTFMODE fMode, const char *pszBasePath,
417 char *pszBuf, size_t cchBuf)
418{
419 /*
420 * Check assumptions made by the code below.
421 */
422 size_t const cchBasePath = strlen(pszBasePath);
423 AssertReturnStmt(cchBasePath < RTPATH_MAX - 10U, errno = ENAMETOOLONG, -1);
424
425 ssize_t rcRet;
426 PRTDIR pDir;
427 int rc = RTDirOpen(&pDir, pszBasePath);
428 if (RT_SUCCESS(rc))
429 {
430 char szPath[RTPATH_MAX]; /** @todo 4K per recursion - can easily be optimized away by passing it along pszBasePath
431 and only remember the length. */
432 memcpy(szPath, pszBasePath, cchBasePath + 1);
433
434 for (;;)
435 {
436 RTDIRENTRYEX Entry;
437 rc = RTDirReadEx(pDir, &Entry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
438 if (RT_FAILURE(rc))
439 {
440 errno = rc == VERR_NO_MORE_FILES
441 ? ENOENT
442 : rc == VERR_BUFFER_OVERFLOW
443 ? EOVERFLOW
444 : EIO;
445 rcRet = -1;
446 break;
447 }
448 if (RTFS_IS_SYMLINK(Entry.Info.Attr.fMode))
449 continue;
450
451 /* Do the matching. */
452 if ( Entry.Info.Attr.u.Unix.Device == DevNum
453 && (Entry.Info.Attr.fMode & RTFS_TYPE_MASK) == fMode)
454 {
455 rcRet = rtLinuxConstructPath(pszBuf, cchBuf, pszBasePath, "%s", Entry.szName);
456 break;
457 }
458
459 /* Recurse into subdirectories. */
460 if (!RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode))
461 continue;
462 if (Entry.szName[0] == '.')
463 continue;
464
465 szPath[cchBasePath] = '\0';
466 rc = RTPathAppend(szPath, sizeof(szPath) - 1, Entry.szName); /* -1: for slash */
467 if (RT_FAILURE(rc))
468 {
469 errno = ENAMETOOLONG;
470 rcRet = -1;
471 break;
472 }
473 strcat(&szPath[cchBasePath], "/");
474 rcRet = rtLinuxFindDevicePathRecursive(DevNum, fMode, szPath, pszBuf, cchBuf);
475 if (rcRet >= 0 || errno != ENOENT)
476 break;
477 }
478 RTDirClose(pDir);
479 }
480 else
481 {
482 rcRet = -1;
483 errno = RTErrConvertToErrno(rc);
484 }
485 return rcRet;
486}
487
488
489RTDECL(ssize_t) RTLinuxFindDevicePathV(dev_t DevNum, RTFMODE fMode, char *pszBuf, size_t cchBuf,
490 const char *pszSuggestion, va_list va)
491{
492 AssertReturnStmt(cchBuf >= 2, errno = EINVAL, -1);
493 AssertReturnStmt( fMode == RTFS_TYPE_DEV_CHAR
494 || fMode == RTFS_TYPE_DEV_BLOCK,
495 errno = EINVAL, -1);
496
497 if (pszSuggestion)
498 {
499 /*
500 * Construct the filename and read the link.
501 */
502 char szFilename[RTPATH_MAX];
503 int rc = rtLinuxConstructPathV(szFilename, sizeof(szFilename), "/dev/", pszSuggestion, va);
504 if (rc == -1)
505 return -1;
506
507 /*
508 * Check whether the caller's suggestion was right.
509 */
510 RTFSOBJINFO Info;
511 rc = RTPathQueryInfo(szFilename, &Info, RTFSOBJATTRADD_UNIX);
512 if ( RT_SUCCESS(rc)
513 && Info.Attr.u.Unix.Device == DevNum
514 && (Info.Attr.fMode & RTFS_TYPE_MASK) == fMode)
515 {
516 size_t cchPath = strlen(szFilename);
517 if (cchPath >= cchBuf)
518 {
519 errno = EOVERFLOW;
520 return -1;
521 }
522 memcpy(pszBuf, szFilename, cchPath + 1);
523 return cchPath;
524 }
525
526 /* The suggestion was wrong, fall back on the brute force attack. */
527 }
528
529 return rtLinuxFindDevicePathRecursive(DevNum, fMode, "/dev/", pszBuf, cchBuf);
530}
531
532
533RTDECL(ssize_t) RTLinuxFindDevicePath(dev_t DevNum, RTFMODE fMode, char *pszBuf, size_t cchBuf,
534 const char *pszSuggestion, ...)
535{
536 va_list va;
537 va_start(va, pszSuggestion);
538 int rc = RTLinuxFindDevicePathV(DevNum, fMode, pszBuf, cchBuf, pszSuggestion, va);
539 va_end(va);
540 return rc;
541}
542
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