NCBI C++ ToolKit
ncbi_os_unix.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_os_unix.cpp 97378 2022-07-12 04:12:50Z lavr $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Anton Lavrentiev
27  *
28  * File Description: UNIX specifics
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 #include <corelib/error_codes.hpp>
34 #include <corelib/ncbistr.hpp>
35 #include <corelib/ncbifile.hpp>
36 #include "ncbi_os_unix_p.hpp"
37 
38 #include <string.h>
39 
40 #include <errno.h>
41 #include <dirent.h> // opendir(), readdir()
42 #include <unistd.h>
43 
44 
45 #define NCBI_USE_ERRCODE_X Corelib_Unix
46 
47 
48 // Some initial defaults for faster lookups
49 #define PWD_BUF 1024
50 #define GRP_BUF 8192
51 #define MAX_TRY 3 // must be at least 2
52 
53 
55 
56 
58 {
59  string user;
60 
61 #if defined(NCBI_OS_SOLARIS) || \
62  (defined(HAVE_GETPWUID) && !defined(NCBI_HAVE_GETPWUID_R))
63  // NB: getpwuid() is MT-safe on Solaris
64  const struct passwd* pwd = getpwuid(uid);
65  if (pwd && pwd->pw_name) {
66  user.assign(pwd->pw_name);
67  }
68 
69 #elif defined(NCBI_HAVE_GETPWUID_R)
70  struct passwd* pwd;
71 
72  char x_buf[sizeof(*pwd) + PWD_BUF];
73  size_t size = PWD_BUF;
74  char* buf = x_buf;
75 
76  for (int n = 0; n < MAX_TRY; ++n) {
77  pwd = (struct passwd*) buf;
78 
79 # if NCBI_HAVE_GETPWUID_R == 4
80  // obsolete but still existing
81  pwd = getpwuid_r(uid, pwd, (char*)(pwd + 1), size);
82 
83 # elif NCBI_HAVE_GETPWUID_R == 5
84  /* POSIX-conforming */
85  int x_errno = getpwuid_r(uid, pwd, (char*)(pwd + 1), size, &pwd);
86  if (x_errno) {
87  errno = x_errno;
88  pwd = 0;
89  }
90 
91 # else
92 # error "Unknown value of NCBI_HAVE_GETPWUID_R: 4 or 5 expected."
93 
94 # endif //NCBI_HAVE_GETPWUID_R
95 
96  if (pwd || errno != ERANGE) {
97  break;
98  }
99 
100  if (n == 0) {
101  size_t maxsize;
102 # ifdef _SC_GETPW_R_SIZE_MAX
103  long sc = sysconf(_SC_GETPW_R_SIZE_MAX);
104  maxsize = sc > 0 ? (size_t) sc : size;
105 # else
106  maxsize = 0;
107 # endif //_SC_GETPW_R_SIZE_MAX
108  ERR_POST_ONCE((!maxsize || size < maxsize ? Error : Critical) <<
109  "getpwuid_r() parse buffer too small ("
110  NCBI_AS_STRING(PWD_BUF) "), please enlarge it"
111  + string(size < maxsize
112  ? " up to at least "
113  + NStr::NumericToString(maxsize)
114  : "!"));
115  _ASSERT(buf == x_buf);
116  if (size < maxsize) {
117  size = maxsize;
118  buf = new char[sizeof(*pwd) + size];
119  continue;
120  }
121  } else if (n == MAX_TRY - 1) {
122  ERR_POST_ONCE(Critical << "getpwuid_r() parse buffer too small ("
123  << NStr::NumericToString(size) << ")!");
124  break;
125  } else {
126  _ASSERT(buf != x_buf);
127  delete[] buf;
128  }
129 
130  size <<= 1;
131  buf = new char[sizeof(*pwd) + size];
132  }
133 
134  if (pwd && pwd->pw_name) {
135  user.assign(pwd->pw_name);
136  }
137  if (buf != x_buf) {
138  delete[] buf;
139  }
140 
141 #endif
142 
143  return user;
144 }
145 
146 
147 uid_t CUnixFeature::GetUserUIDByName(const string& user)
148 {
149  uid_t uid;
150 
151 #if defined(NCBI_OS_SOLARIS) || \
152  (defined(HAVE_GETPWUID) && !defined(NCBI_HAVE_GETPWUID_R))
153  // NB: getpwnam() is MT-safe on Solaris
154  const struct passwd* pwd = getpwnam(user.c_str());
155  uid = pwd ? pwd->pw_uid : (uid_t)(-1);
156 
157 #elif defined(NCBI_HAVE_GETPWUID_R)
158  struct passwd* pwd;
159 
160  char x_buf[sizeof(*pwd) + PWD_BUF];
161  size_t size = PWD_BUF;
162  char* buf = x_buf;
163 
164  for (int n = 0; n < MAX_TRY; ++n) {
165  pwd = (struct passwd*) buf;
166 
167 # if NCBI_HAVE_GETPWUID_R == 4
168  // obsolete but still existing
169  pwd = getpwnam_r(user.c_str(), pwd, (char*)(pwd + 1), size);
170 
171 # elif NCBI_HAVE_GETPWUID_R == 5
172  // POSIX-conforming
173  int x_errno = getpwnam_r(user.c_str(),
174  pwd, (char*)(pwd + 1), size, &pwd);
175  if (x_errno) {
176  errno = x_errno;
177  pwd = 0;
178  }
179 
180 # else
181 # error "Unknown value of NCBI_HAVE_GETPWUID_R: 4 or 5 expected."
182 
183 # endif //NCBI_HAVE_GETPWUID_R
184 
185  if (pwd || errno != ERANGE) {
186  break;
187  }
188 
189  if (n == 0) {
190  size_t maxsize;
191 # ifdef _SC_GETPW_R_SIZE_MAX
192  long sc = sysconf(_SC_GETPW_R_SIZE_MAX);
193  maxsize = sc > 0 ? (size_t) sc : size;
194 # else
195  maxsize = 0;
196 # endif //_SC_GETPW_R_SIZE_MAX
197  ERR_POST_ONCE((!maxsize || size < maxsize ? Error : Critical) <<
198  "getpwnam_r() parse buffer too small ("
199  NCBI_AS_STRING(PWD_BUF) "), please enlarge it"
200  + string(size < maxsize
201  ? " up to at least "
202  + NStr::NumericToString(maxsize)
203  : "!"));
204  _ASSERT(buf == x_buf);
205  if (size < maxsize) {
206  size = maxsize;
207  buf = new char[sizeof(*pwd) + size];
208  continue;
209  }
210  } else if (n == MAX_TRY - 1) {
211  ERR_POST_ONCE(Critical << "getpwnam_r() parse buffer too small ("
212  << NStr::NumericToString(size) << ")!");
213  break;
214  } else {
215  _ASSERT(buf != x_buf);
216  delete[] buf;
217  }
218 
219  size <<= 1;
220  buf = new char[sizeof(*pwd) + size];
221  }
222 
223  uid = pwd ? pwd->pw_uid : (uid_t)(-1);
224 
225  if (buf != x_buf) {
226  delete[] buf;
227  }
228 
229 #else
230  uid = (uid_t)(-1);
231 
232 #endif
233 
234  return uid;
235 }
236 
237 
239 {
240  string group;
241 
242 #if defined(NCBI_OS_SOLARIS) || \
243  (defined(HAVE_GETPWUID) && !defined(NCBI_HAVE_GETPWUID_R))
244  // NB: getgrgid() is MT-safe on Solaris
245  const struct group* grp = getgrgid(gid);
246  if (grp && grp->gr_name) {
247  group.assign(grp->gr_name);
248  }
249 
250 #elif defined(NCBI_HAVE_GETPWUID_R)
251  struct group* grp;
252 
253  char x_buf[sizeof(*grp) + GRP_BUF];
254  size_t size = GRP_BUF;
255  char* buf = x_buf;
256 
257  for (int n = 0; n < MAX_TRY; ++n) {
258  grp = (struct group*) buf;
259 
260 # if NCBI_HAVE_GETPWUID_R == 4
261  // obsolete but still existing
262  grp = getgrgid_r(gid, grp, (char*)(grp + 1), size);
263 
264 # elif NCBI_HAVE_GETPWUID_R == 5
265  // POSIX-conforming
266  int x_errno = getgrgid_r(gid, grp,(char*)(grp + 1), size, &grp);
267  if (x_errno) {
268  errno = x_errno;
269  grp = 0;
270  }
271 
272 # else
273 # error "Unknown value of NCBI_HAVE_GETPWUID_R: 4 or 5 expected."
274 
275 # endif //NCBI_HAVE_GETPWUID_R
276 
277  if (grp || errno != ERANGE) {
278  break;
279  }
280 
281  if (n == 0) {
282  size_t maxsize;
283 # ifdef _SC_GETGR_R_SIZE_MAX
284  long sc = sysconf(_SC_GETGR_R_SIZE_MAX);
285  maxsize = sc > 0 ? (size_t) sc : size;
286 # else
287  maxsize = 0;
288 # endif //_SC_GETGR_R_SIZE_MAX
289  ERR_POST_ONCE((!maxsize || size < maxsize ? Error : Critical) <<
290  "getgrgid_r() parse buffer too small ("
291  NCBI_AS_STRING(GRP_BUF) "), please enlarge it"
292  + string(size < maxsize
293  ? " up to at least "
294  + NStr::NumericToString(maxsize)
295  : "!"));
296  _ASSERT(buf == x_buf);
297  if (size < maxsize) {
298  size = maxsize;
299  buf = new char[sizeof(*grp) + size];
300  continue;
301  }
302  } else if (n == MAX_TRY - 1) {
303  ERR_POST_ONCE(Critical << "getgrgid_r() parse buffer too small ("
304  << NStr::NumericToString(size) << ")!");
305  break;
306  } else {
307  _ASSERT(buf != x_buf);
308  delete[] buf;
309  }
310 
311  size <<= 1;
312  buf = new char[sizeof(*grp) + size];
313  }
314 
315  if (grp && grp->gr_name) {
316  group.assign(grp->gr_name);
317  }
318 
319  if (buf != x_buf) {
320  delete[] buf;
321  }
322 
323 #endif
324 
325  return group;
326 }
327 
328 
329 gid_t CUnixFeature::GetGroupGIDByName(const string& group)
330 {
331  gid_t gid;
332 
333 #if defined(NCBI_OS_SOLARIS) || \
334  (defined(HAVE_GETPWUID) && !defined(NCBI_HAVE_GETPWUID_R))
335  // NB: getgrnam() is MT-safe on Solaris
336  const struct group* grp = getgrnam(group.c_str());
337  gid = grp ? grp->gr_gid : (gid_t)(-1);
338 
339 #elif defined(NCBI_HAVE_GETPWUID_R)
340  struct group* grp;
341 
342  char x_buf[sizeof(*grp) + GRP_BUF];
343  size_t size = GRP_BUF;
344  char* buf = x_buf;
345 
346  for (int n = 0; n < MAX_TRY; ++n) {
347  grp = (struct group*) buf;
348 
349 # if NCBI_HAVE_GETPWUID_R == 4
350  // obsolete but still existing
351  grp = getgrnam_r(group.c_str(), grp, (char*)(grp + 1), size);
352 
353 # elif NCBI_HAVE_GETPWUID_R == 5
354  // POSIX-conforming
355  int x_errno = getgrnam_r(group.c_str(),
356  grp, (char*)(grp + 1), size, &grp);
357  if (x_errno) {
358  errno = x_errno;
359  grp = 0;
360  }
361 
362 # else
363 # error "Unknown value of NCBI_HAVE_GETPWUID_R: 4 or 5 expected."
364 
365 # endif //NCBI_HAVE_GETPWUID_R
366 
367  if (grp || errno != ERANGE) {
368  break;
369  }
370 
371  if (n == 0) {
372  size_t maxsize;
373 # ifdef _SC_GETGR_R_SIZE_MAX
374  long sc = sysconf(_SC_GETGR_R_SIZE_MAX);
375  maxsize = sc > 0 ? (size_t) sc : size;
376 # else
377  maxsize = 0;
378 # endif //_SC_GETGR_R_SIZE_MAX
379  ERR_POST_ONCE((!maxsize || size < maxsize ? Error : Critical) <<
380  "getgrnam_r() parse buffer too small ("
381  NCBI_AS_STRING(GRP_BUF) "), please enlarge it"
382  + string(size < maxsize
383  ? " up to at least "
384  + NStr::NumericToString(maxsize)
385  : "!"));
386  _ASSERT(buf == x_buf);
387  if (size < maxsize) {
388  size = maxsize;
389  buf = new char[sizeof(*grp) + size];
390  continue;
391  }
392  } else if (n == MAX_TRY - 1) {
393  ERR_POST_ONCE(Critical << "getgrnam_r() parse buffer too small ("
394  << NStr::NumericToString(size) << ")!");
395  break;
396  } else {
397  _ASSERT(buf != x_buf);
398  delete[] buf;
399  }
400 
401  size <<= 1;
402  buf = new char[sizeof(*grp) + size];
403  }
404 
405  gid = grp ? grp->gr_gid : (gid_t)(-1);
406 
407  if (buf != x_buf) {
408  delete[] buf;
409  }
410 
411 #else
412  gid = (gid_t)(-1);
413 
414 #endif
415  return gid;
416 }
417 
418 
419 
420 #if defined(NCBI_OS_LINUX)
421 
422 #define PROCFS(pid, file) \
423  "/proc/" + (!pid ? "self" : NStr::NumericToString(pid)) + "/" + file
424 
425 // Helper method to read file, to avoid races
426 inline
427 size_t s_ReadFile(const string& filename, char* buf, size_t size)
428 {
429  size_t n = 0;
430  CFileIO f;
431  try {
432  f.Open(filename, CFileIO::eOpen, CFileIO::eRead);
433  n = f.Read(buf, size);
434  buf[n] = '\0';
435  }
436  catch(CFileException& e) {
437  }
438  f.Close();
439  return n;
440 }
441 
442 // Find "name" string in "buf" and parse value following it.
443 // Put result to "value". But it shouldn't be less than "min_value".
444 // All values are in kB.
445 inline
446 size_t s_ParseStatusVmValue(const char* name, char* buf, size_t min_value = 0)
447 {
451  char* pos = strstr(buf, name);
452  if (!pos) {
453  return 0;
454  }
455  size_t n = NStr::StringToNumeric<size_t>(pos+strlen(name)+1/*:*/, flags) * 1024;
456  return (n < min_value) ? min_value : n;
457 }
458 
460 {
461  char buf[2048];
462  size_t n;
463 
464  // Parse /proc/<pid>/statm
465 
466  n = s_ReadFile(PROCFS(pid,"statm"), buf, sizeof(buf));
467  if ( n ) {
468  CNcbiIstrstream is(string(buf, n));
469  is >> usage.total >> usage.resident >> usage.shared >> usage.text >> usage.lib /*>> usage.data*/;
470  const unsigned long page_size = CSystemInfo::GetVirtualMemoryPageSize();
471  usage.total *= page_size;
472  usage.resident *= page_size;
473  usage.shared *= page_size;
474  usage.text *= page_size;
475  usage.lib *= page_size;
476  //usage.data *= page_size;
477  }
478 
479  // Additionally parse /proc/<pid>/status
480 
481  n = s_ReadFile(PROCFS(pid,"status"), buf, sizeof(buf));
482  if ( n ) {
483  // Find starting point for all "Vm*" values
484  char* pos = strstr(buf, "Vm");
485  if (!pos) {
486  return true;
487  }
488  usage.total_peak = s_ParseStatusVmValue("VmPeak", pos, usage.total);
489  usage.resident_peak = s_ParseStatusVmValue("VmHWM", pos, usage.resident);
490  usage.data = s_ParseStatusVmValue("VmData", pos);
491  usage.swap = s_ParseStatusVmValue("VmSwap", pos);
492  if (!usage.text) {
493  usage.text = s_ParseStatusVmValue("VmExe", pos);
494  }
495  if (!usage.lib) {
496  usage.lib = s_ParseStatusVmValue("VmLib", pos);
497  }
498  if (!usage.stack) {
499  usage.stack = s_ParseStatusVmValue("VmStk", pos);
500  }
501  }
502  return true;
503 }
504 
505 
506 int CLinuxFeature::GetThreadCount(pid_t pid)
507 {
508  int n = 0;
509  string name = PROCFS(pid, "task");
510  DIR* dir = opendir(name.c_str());
511  if (dir) {
512  while (readdir(dir) != NULL) {
513  ++n;
514  }
515  closedir(dir);
516  n -= 2; // '.' and '..'
517  if (n > 0) {
518  return n;
519  }
520  }
522  return -1;
523 }
524 
525 
526 int CLinuxFeature::GetFileDescriptorsCount(pid_t pid)
527 {
528  int n = 0;
529  string name = PROCFS(pid, "fd");
530  DIR* dir = opendir(name.c_str());
531  if (dir) {
532  while (readdir(dir) != NULL) {
533  ++n;
534  }
535  closedir(dir);
536  n -= 3; // '.', '..' and the one for opendir()
537  if (n >= 0) {
538  return n;
539  }
540  }
542  return -1;
543 }
544 
545 
546 CLinuxFeature::CProcStat::CProcStat(pid_t pid)
547 {
548  m_Parsed = false;
549 
550  char buf[2048];
551  size_t n = s_ReadFile(PROCFS(pid,"stat"), buf, sizeof(buf));
552 
553  m_Storage.reserve(n);
554  m_Storage.assign(buf, n);
555  m_List.clear();
556  m_List.reserve(55);
557 
558  size_t p1 = m_Storage.find('(');
559  if (p1 == NPOS) {
560  return;
561  }
562  m_List.push_back(CTempString(m_Storage, 0, p1-1));
563  size_t p2 = m_Storage.find(')', p1+1);
564  if (p2 == NPOS) {
565  return;
566  }
567  m_List.push_back(CTempString(m_Storage, p1+1, p2-p1-1));
568  NStr::Split(m_Storage.data() + p2 + 1, " ", m_List);
569 
570  m_Parsed = true;
571  return;
572 }
573 
574 #endif
575 
576 
const char * usage
CFileException –.
Definition: ncbifile.hpp:136
Class for support low level input/output for files.
Definition: ncbifile.hpp:3475
static unsigned long GetVirtualMemoryPageSize(void)
Return virtual memory page size.
CTempString implements a light-weight string on top of a storage buffer whose lifetime management is ...
Definition: tempstr.hpp:65
static string GetUserNameByUID(uid_t uid)
Look up user name by given numeric user ID.
static gid_t GetGroupGIDByName(const string &group)
Look up numeric group ID by symbolic group name.
static string GetGroupNameByGID(gid_t gid)
Look up group name by given numeric group ID.
static uid_t GetUserUIDByName(const string &user)
Look up numeric user ID by symbolic user name.
The NCBI C++ standard methods for dealing with std::string.
static uch flags
#define NULL
Definition: ncbistd.hpp:225
#define ERR_POST_ONCE(message)
Error posting only once during program execution.
Definition: ncbidiag.hpp:602
void Critical(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1203
void Error(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1197
static void Set(ECode code)
Set last error using native error code enum.
Definition: ncbierror.cpp:160
@ eUnknown
Unknown error.
Definition: ncbierror.hpp:138
@ eRead
File can be read.
Definition: ncbifile.hpp:3435
@ eOpen
Open an existing file, or create a new one.
Definition: ncbifile.hpp:3425
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
#define NCBI_AS_STRING(value)
Convert some value to string even if this value is macro itself.
Definition: ncbistl.hpp:146
static list< string > & Split(const CTempString str, const CTempString delim, list< string > &arr, TSplitFlags flags=0, vector< SIZE_TYPE > *token_pos=NULL)
Split a string using specified delimiters.
Definition: ncbistr.cpp:3461
#define NPOS
Definition: ncbistr.hpp:133
int TStringToNumFlags
Bitwise OR of "EStringToNumFlags".
Definition: ncbistr.hpp:311
static enable_if< is_arithmetic< TNumeric >::value||is_convertible< TNumeric, Int8 >::value, string >::type NumericToString(TNumeric value, TNumToStringFlags flags=0, int base=10)
Convert numeric value to string.
Definition: ncbistr.hpp:673
@ fConvErr_NoThrow
Do not throw an exception on error.
Definition: ncbistr.hpp:285
@ fAllowTrailingSymbols
Ignore trailing non-numerics characters.
Definition: ncbistr.hpp:298
@ fAllowLeadingSpaces
Ignore leading spaces in converted string.
Definition: ncbistr.hpp:294
Definition of all error codes used in corelib (xncbi.lib).
char * buf
yy_size_t n
const struct ncbi::grid::netcache::search::fields::SIZE size
#define min_value(a, b)
Definition: ncbi_c_log.c:140
#define MAX_TRY
#define GRP_BUF
#define PWD_BUF
Private UNIX specific features.
bool GetMemoryUsage(size_t *total, size_t *resident, size_t *shared)
Defines classes: CDirEntry, CFile, CDir, CSymLink, CMemoryFile, CFileUtil, CFileLock,...
double f(double x_, const double &y_)
Definition: njn_root.hpp:188
Process memory usage information, in bytes.
#define _ASSERT
Modified on Wed May 29 18:37:51 2024 by modify_doxy.py rev. 669887