root/lib/common/io.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk__build_path
  2. crm_build_path
  3. pcmk__real_path
  4. pcmk__series_filename
  5. pcmk__read_series_sequence
  6. pcmk__write_series_sequence
  7. pcmk__chown_series_sequence
  8. pcmk__daemon_user_can_write
  9. pcmk__daemon_group_can_write
  10. pcmk__daemon_can_write
  11. pcmk__sync_directory
  12. pcmk__file_contents
  13. pcmk__write_sync
  14. pcmk__set_nonblocking
  15. pcmk__get_tmpdir
  16. pcmk__close_fds_in_child

   1 /*
   2  * Copyright 2004-2020 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #ifndef _GNU_SOURCE
  13 #  define _GNU_SOURCE
  14 #endif
  15 
  16 #include <sys/param.h>
  17 #include <sys/types.h>
  18 #include <sys/stat.h>
  19 #include <sys/resource.h>
  20 
  21 #include <stdio.h>
  22 #include <unistd.h>
  23 #include <string.h>
  24 #include <stdlib.h>
  25 #include <fcntl.h>
  26 #include <dirent.h>
  27 #include <errno.h>
  28 #include <limits.h>
  29 #include <pwd.h>
  30 #include <grp.h>
  31 
  32 #include <crm/crm.h>
  33 #include <crm/common/util.h>
  34 
  35 /*!
  36  * \internal
  37  * \brief Create a directory, including any parent directories needed
  38  *
  39  * \param[in] path_c Pathname of the directory to create
  40  * \param[in] mode Permissions to be used (with current umask) when creating
  41  *
  42  * \return Standard Pacemaker return code
  43  */
  44 int
  45 pcmk__build_path(const char *path_c, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47     int offset = 1, len = 0;
  48     int rc = pcmk_rc_ok;
  49     char *path = strdup(path_c);
  50 
  51     // cppcheck seems not to understand the abort logic in CRM_CHECK
  52     // cppcheck-suppress memleak
  53     CRM_CHECK(path != NULL, return -ENOMEM);
  54     for (len = strlen(path); offset < len; offset++) {
  55         if (path[offset] == '/') {
  56             path[offset] = 0;
  57             if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
  58                 rc = errno;
  59                 goto done;
  60             }
  61             path[offset] = '/';
  62         }
  63     }
  64     if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
  65         rc = errno;
  66     }
  67 done:
  68     free(path);
  69     return rc;
  70 }
  71 
  72 /*!
  73  * \brief Create a directory, including any parent directories needed
  74  *
  75  * \param[in] path_c Pathname of the directory to create
  76  * \param[in] mode Permissions to be used (with current umask) when creating
  77  *
  78  * \note This logs errors but does not return them to the caller.
  79  */
  80 void
  81 crm_build_path(const char *path_c, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83     int rc = pcmk__build_path(path_c, mode);
  84 
  85     if (rc != pcmk_rc_ok) {
  86         crm_err("Could not create directory '%s': %s",
  87                 path_c, pcmk_rc_str(rc));
  88     }
  89 }
  90 
  91 /*!
  92  * \internal
  93  * \brief Return canonicalized form of a path name
  94  *
  95  * \param[in]  path           Pathname to canonicalize
  96  * \param[out] resolved_path  Where to store canonicalized pathname
  97  *
  98  * \return Standard Pacemaker return code
  99  * \note The caller is responsible for freeing \p resolved_path on success.
 100  * \note This function exists because not all C library versions of
 101  *       realpath(path, resolved_path) support a NULL resolved_path.
 102  */
 103 int
 104 pcmk__real_path(const char *path, char **resolved_path)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106     CRM_CHECK((path != NULL) && (resolved_path != NULL), return EINVAL);
 107 
 108 #if _POSIX_VERSION >= 200809L
 109     /* Recent C libraries can dynamically allocate memory as needed */
 110     *resolved_path = realpath(path, NULL);
 111     return (*resolved_path == NULL)? errno : pcmk_rc_ok;
 112 
 113 #elif defined(PATH_MAX)
 114     /* Older implementations require pre-allocated memory */
 115     /* (this is less desirable because PATH_MAX may be huge or not defined) */
 116     *resolved_path = malloc(PATH_MAX);
 117     if ((*resolved_path == NULL) || (realpath(path, *resolved_path) == NULL)) {
 118         return errno;
 119     }
 120     return pcmk_rc_ok;
 121 #else
 122     *resolved_path = NULL;
 123     return ENOTSUP;
 124 #endif
 125 }
 126 
 127 /*!
 128  * \internal
 129  * \brief Create a file name using a sequence number
 130  *
 131  * \param[in] directory  Directory that contains the file series
 132  * \param[in] series     Start of file name
 133  * \param[in] sequence   Sequence number
 134  * \param[in] bzip       Whether to use ".bz2" instead of ".raw" as extension
 135  *
 136  * \return Newly allocated file path (asserts on error, so always non-NULL)
 137  * \note The caller is responsible for freeing the return value.
 138  */
 139 char *
 140 pcmk__series_filename(const char *directory, const char *series, int sequence,
     /* [previous][next][first][last][top][bottom][index][help] */
 141                       bool bzip)
 142 {
 143     CRM_ASSERT((directory != NULL) && (series != NULL));
 144     return crm_strdup_printf("%s/%s-%d.%s", directory, series, sequence,
 145                              (bzip? "bz2" : "raw"));
 146 }
 147 
 148 /*!
 149  * \internal
 150  * \brief Read sequence number stored in a file series' .last file
 151  *
 152  * \param[in]  directory  Directory that contains the file series
 153  * \param[in]  series     Start of file name
 154  * \param[out] seq        Where to store the sequence number
 155  *
 156  * \return Standard Pacemaker return code
 157  */
 158 int
 159 pcmk__read_series_sequence(const char *directory, const char *series,
     /* [previous][next][first][last][top][bottom][index][help] */
 160                            unsigned int *seq)
 161 {
 162     int rc;
 163     FILE *fp = NULL;
 164     char *series_file = NULL;
 165 
 166     if ((directory == NULL) || (series == NULL) || (seq == NULL)) {
 167         return EINVAL;
 168     }
 169 
 170     series_file = crm_strdup_printf("%s/%s.last", directory, series);
 171     fp = fopen(series_file, "r");
 172     if (fp == NULL) {
 173         rc = errno;
 174         crm_debug("Could not open series file %s: %s",
 175                   series_file, strerror(rc));
 176         free(series_file);
 177         return rc;
 178     }
 179     errno = 0;
 180     if (fscanf(fp, "%u", seq) != 1) {
 181         rc = (errno == 0)? pcmk_rc_unknown_format : errno;
 182         crm_debug("Could not read sequence number from series file %s: %s",
 183                   series_file, pcmk_rc_str(rc));
 184         fclose(fp);
 185         return rc;
 186     }
 187     fclose(fp);
 188     crm_trace("Found last sequence number %u in series file %s",
 189               *seq, series_file);
 190     free(series_file);
 191     return pcmk_rc_ok;
 192 }
 193 
 194 /*!
 195  * \internal
 196  * \brief Write sequence number to a file series' .last file
 197  *
 198  * \param[in] directory  Directory that contains the file series
 199  * \param[in] series     Start of file name
 200  * \param[in] sequence   Sequence number to write
 201  * \param[in] max        Maximum sequence value, after which it is reset to 0
 202  *
 203  * \note This function logs some errors but does not return any to the caller
 204  */
 205 void
 206 pcmk__write_series_sequence(const char *directory, const char *series,
     /* [previous][next][first][last][top][bottom][index][help] */
 207                             unsigned int sequence, int max)
 208 {
 209     int rc = 0;
 210     FILE *file_strm = NULL;
 211     char *series_file = NULL;
 212 
 213     CRM_CHECK(directory != NULL, return);
 214     CRM_CHECK(series != NULL, return);
 215 
 216     if (max == 0) {
 217         return;
 218     }
 219     if (max > 0 && sequence >= max) {
 220         sequence = 0;
 221     }
 222 
 223     series_file = crm_strdup_printf("%s/%s.last", directory, series);
 224     file_strm = fopen(series_file, "w");
 225     if (file_strm != NULL) {
 226         rc = fprintf(file_strm, "%u", sequence);
 227         if (rc < 0) {
 228             crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
 229         }
 230 
 231     } else {
 232         crm_err("Cannot open series file %s for writing", series_file);
 233     }
 234 
 235     if (file_strm != NULL) {
 236         fflush(file_strm);
 237         fclose(file_strm);
 238     }
 239 
 240     crm_trace("Wrote %d to %s", sequence, series_file);
 241     free(series_file);
 242 }
 243 
 244 /*!
 245  * \internal
 246  * \brief Change the owner and group of a file series' .last file
 247  *
 248  * \param[in] dir  Directory that contains series
 249  * \param[in] uid  User ID of desired file owner
 250  * \param[in] gid  Group ID of desired file group
 251  *
 252  * \return Standard Pacemaker return code
 253  * \note The caller must have the appropriate privileges.
 254  */
 255 int
 256 pcmk__chown_series_sequence(const char *directory, const char *series,
     /* [previous][next][first][last][top][bottom][index][help] */
 257                             uid_t uid, gid_t gid)
 258 {
 259     char *series_file = NULL;
 260     int rc = pcmk_rc_ok;
 261 
 262     if ((directory == NULL) || (series == NULL)) {
 263         return EINVAL;
 264     }
 265     series_file = crm_strdup_printf("%s/%s.last", directory, series);
 266     if (chown(series_file, uid, gid) < 0) {
 267         rc = errno;
 268     }
 269     free(series_file);
 270     return rc;
 271 }
 272 
 273 static bool
 274 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
     /* [previous][next][first][last][top][bottom][index][help] */
 275 {
 276     struct passwd *sys_user = NULL;
 277 
 278     errno = 0;
 279     sys_user = getpwnam(CRM_DAEMON_USER);
 280     if (sys_user == NULL) {
 281         crm_notice("Could not find user %s: %s",
 282                    CRM_DAEMON_USER, pcmk_strerror(errno));
 283         return FALSE;
 284     }
 285     if (target_stat->st_uid != sys_user->pw_uid) {
 286         crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
 287                    target_name, CRM_DAEMON_USER, sys_user->pw_uid,
 288                    target_stat->st_uid);
 289         return FALSE;
 290     }
 291     if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
 292         crm_notice("%s is not readable and writable by user %s "
 293                    CRM_XS " st_mode=0%lo",
 294                    target_name, CRM_DAEMON_USER,
 295                    (unsigned long) target_stat->st_mode);
 296         return FALSE;
 297     }
 298     return TRUE;
 299 }
 300 
 301 static bool
 302 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304     struct group *sys_grp = NULL;
 305 
 306     errno = 0;
 307     sys_grp = getgrnam(CRM_DAEMON_GROUP);
 308     if (sys_grp == NULL) {
 309         crm_notice("Could not find group %s: %s",
 310                    CRM_DAEMON_GROUP, pcmk_strerror(errno));
 311         return FALSE;
 312     }
 313 
 314     if (target_stat->st_gid != sys_grp->gr_gid) {
 315         crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
 316                    target_name, CRM_DAEMON_GROUP,
 317                    sys_grp->gr_gid, target_stat->st_gid);
 318         return FALSE;
 319     }
 320 
 321     if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
 322         crm_notice("%s is not readable and writable by group %s "
 323                    CRM_XS " st_mode=0%lo",
 324                    target_name, CRM_DAEMON_GROUP,
 325                    (unsigned long) target_stat->st_mode);
 326         return FALSE;
 327     }
 328     return TRUE;
 329 }
 330 
 331 /*!
 332  * \internal
 333  * \brief Check whether a directory or file is writable by the cluster daemon
 334  *
 335  * Return true if either the cluster daemon user or cluster daemon group has
 336  * write permission on a specified file or directory.
 337  *
 338  * \param[in] dir      Directory to check (this argument must be specified, and
 339  *                     the directory must exist)
 340  * \param[in] file     File to check (only the directory will be checked if this
 341  *                     argument is not specified or the file does not exist)
 342  *
 343  * \return true if target is writable by cluster daemon, false otherwise
 344  */
 345 bool
 346 pcmk__daemon_can_write(const char *dir, const char *file)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348     int s_res = 0;
 349     struct stat buf;
 350     char *full_file = NULL;
 351     const char *target = NULL;
 352 
 353     // Caller must supply directory
 354     CRM_ASSERT(dir != NULL);
 355 
 356     // If file is given, check whether it exists as a regular file
 357     if (file != NULL) {
 358         full_file = crm_strdup_printf("%s/%s", dir, file);
 359         target = full_file;
 360 
 361         s_res = stat(full_file, &buf);
 362         if (s_res < 0) {
 363             crm_notice("%s not found: %s", target, pcmk_strerror(errno));
 364             free(full_file);
 365             full_file = NULL;
 366             target = NULL;
 367 
 368         } else if (S_ISREG(buf.st_mode) == FALSE) {
 369             crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
 370                     target, (unsigned long) buf.st_mode);
 371             free(full_file);
 372             return false;
 373         }
 374     }
 375 
 376     // If file is not given, ensure dir exists as directory
 377     if (target == NULL) {
 378         target = dir;
 379         s_res = stat(dir, &buf);
 380         if (s_res < 0) {
 381             crm_err("%s not found: %s", dir, pcmk_strerror(errno));
 382             return false;
 383 
 384         } else if (S_ISDIR(buf.st_mode) == FALSE) {
 385             crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
 386                     dir, (unsigned long) buf.st_mode);
 387             return false;
 388         }
 389     }
 390 
 391     if (!pcmk__daemon_user_can_write(target, &buf)
 392         && !pcmk__daemon_group_can_write(target, &buf)) {
 393 
 394         crm_err("%s must be owned and writable by either user %s or group %s "
 395                 CRM_XS " st_mode=0%lo",
 396                 target, CRM_DAEMON_USER, CRM_DAEMON_GROUP,
 397                 (unsigned long) buf.st_mode);
 398         free(full_file);
 399         return false;
 400     }
 401 
 402     free(full_file);
 403     return true;
 404 }
 405 
 406 /*!
 407  * \internal
 408  * \brief Flush and sync a directory to disk
 409  *
 410  * \param[in] name Directory to flush and sync
 411  * \note This function logs errors but does not return them to the caller
 412  */
 413 void
 414 pcmk__sync_directory(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 415 {
 416     int fd;
 417     DIR *directory;
 418 
 419     directory = opendir(name);
 420     if (directory == NULL) {
 421         crm_perror(LOG_ERR, "Could not open %s for syncing", name);
 422         return;
 423     }
 424 
 425     fd = dirfd(directory);
 426     if (fd < 0) {
 427         crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
 428         return;
 429     }
 430 
 431     if (fsync(fd) < 0) {
 432         crm_perror(LOG_ERR, "Could not sync %s", name);
 433     }
 434     if (closedir(directory) < 0) {
 435         crm_perror(LOG_ERR, "Could not close %s after fsync", name);
 436     }
 437 }
 438 
 439 /*!
 440  * \internal
 441  * \brief Read the contents of a file
 442  *
 443  * \param[in]  filename  Name of file to read
 444  * \param[out] contents  Where to store file contents
 445  *
 446  * \return Standard Pacemaker return code
 447  * \note On success, the caller is responsible for freeing contents.
 448  */
 449 int
 450 pcmk__file_contents(const char *filename, char **contents)
     /* [previous][next][first][last][top][bottom][index][help] */
 451 {
 452     FILE *fp;
 453     int length, read_len;
 454     int rc = pcmk_rc_ok;
 455 
 456     if ((filename == NULL) || (contents == NULL)) {
 457         return EINVAL;
 458     }
 459 
 460     fp = fopen(filename, "r");
 461     if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
 462         rc = errno;
 463         goto bail;
 464     }
 465 
 466     length = ftell(fp);
 467     if (length < 0) {
 468         rc = errno;
 469         goto bail;
 470     }
 471 
 472     if (length == 0) {
 473         *contents = NULL;
 474     } else {
 475         *contents = calloc(length + 1, sizeof(char));
 476         if (*contents == NULL) {
 477             rc = errno;
 478             goto bail;
 479         }
 480         rewind(fp);
 481         read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
 482         if (read_len != length) {
 483             free(*contents);
 484             *contents = NULL;
 485             rc = EIO;
 486         }
 487     }
 488 
 489 bail:
 490     if (fp != NULL) {
 491         fclose(fp);
 492     }
 493     return rc;
 494 }
 495 
 496 /*!
 497  * \internal
 498  * \brief Write text to a file, flush and sync it to disk, then close the file
 499  *
 500  * \param[in] fd        File descriptor opened for writing
 501  * \param[in] contents  String to write to file
 502  *
 503  * \return Standard Pacemaker return code
 504  */
 505 int
 506 pcmk__write_sync(int fd, const char *contents)
     /* [previous][next][first][last][top][bottom][index][help] */
 507 {
 508     int rc = 0;
 509     FILE *fp = fdopen(fd, "w");
 510 
 511     if (fp == NULL) {
 512         return errno;
 513     }
 514     if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
 515         rc = EIO;
 516     }
 517     if (fflush(fp) != 0) {
 518         rc = errno;
 519     }
 520     if (fsync(fileno(fp)) < 0) {
 521         rc = errno;
 522     }
 523     fclose(fp);
 524     return rc;
 525 }
 526 
 527 /*!
 528  * \internal
 529  * \brief Set a file descriptor to non-blocking
 530  *
 531  * \param[in] fd  File descriptor to use
 532  *
 533  * \return Standard Pacemaker return code
 534  */
 535 int
 536 pcmk__set_nonblocking(int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
 537 {
 538     int flag = fcntl(fd, F_GETFL);
 539 
 540     if (flag < 0) {
 541         return errno;
 542     }
 543     if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
 544         return errno;
 545     }
 546     return pcmk_rc_ok;
 547 }
 548 
 549 /*!
 550  * \internal
 551  * \brief Get directory name for temporary files
 552  *
 553  * Return the value of the TMPDIR environment variable if it is set to a
 554  * full path, otherwise return "/tmp".
 555  *
 556  * \return Name of directory to be used for temporary files
 557  */
 558 const char *
 559 pcmk__get_tmpdir()
     /* [previous][next][first][last][top][bottom][index][help] */
 560 {
 561     const char *dir = getenv("TMPDIR");
 562 
 563     return (dir && (*dir == '/'))? dir : "/tmp";
 564 }
 565 
 566 /*!
 567  * \internal
 568  * \brief Close open file descriptors
 569  *
 570  * Close all file descriptors (except optionally stdin, stdout, and stderr),
 571  * which is a best practice for a new child process forked for the purpose of
 572  * executing an external program.
 573  *
 574  * \param[in] bool  If true, close stdin, stdout, and stderr as well
 575  */
 576 void
 577 pcmk__close_fds_in_child(bool all)
     /* [previous][next][first][last][top][bottom][index][help] */
 578 {
 579     DIR *dir;
 580     struct rlimit rlim;
 581     rlim_t max_fd;
 582     int min_fd = (all? 0 : (STDERR_FILENO + 1));
 583 
 584     /* Find the current process's (soft) limit for open files. getrlimit()
 585      * should always work, but have a fallback just in case.
 586      */
 587     if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
 588         max_fd = rlim.rlim_cur - 1;
 589     } else {
 590         long conf_max = sysconf(_SC_OPEN_MAX);
 591 
 592         max_fd = (conf_max > 0)? conf_max : 1024;
 593     }
 594 
 595     /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
 596      * all open files for the current process, named as the file descriptor.
 597      * Use this if available, because it's more efficient than a shotgun
 598      * approach to closing descriptors.
 599      */
 600 #if SUPPORT_PROCFS
 601     dir = opendir("/proc/self/fd");
 602     if (dir == NULL) {
 603         dir = opendir("/dev/fd");
 604     }
 605 #else
 606     dir = opendir("/dev/fd");
 607 #endif
 608     if (dir != NULL) {
 609         struct dirent *entry;
 610         int dir_fd = dirfd(dir);
 611 
 612         while ((entry = readdir(dir)) != NULL) {
 613             int lpc = atoi(entry->d_name);
 614 
 615             /* How could one of these entries be higher than max_fd, you ask?
 616              * It isn't possible in normal operation, but when run under
 617              * valgrind, valgrind can open high-numbered file descriptors for
 618              * its own use that are higher than the process's soft limit.
 619              * These will show up in the fd directory but aren't closable.
 620              */
 621             if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
 622                 close(lpc);
 623             }
 624         }
 625         closedir(dir);
 626         return;
 627     }
 628 
 629     /* If no fd directory is available, iterate over all possible descriptors.
 630      * This is less efficient due to the overhead of many system calls.
 631      */
 632     for (int lpc = max_fd; lpc >= min_fd; lpc--) {
 633         close(lpc);
 634     }
 635 }

/* [previous][next][first][last][top][bottom][index][help] */