LCOV - code coverage report
Current view: top level - libexcept - file_inheritance.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 9 47 19.1 %
Date: 2022-06-30 20:42:18 Functions: 3 5 60.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : // self
      21             : //
      22             : #include    "./file_inheritance.h"
      23             : 
      24             : 
      25             : // C++ includes
      26             : //
      27             : #include    <fstream>
      28             : #include    <iostream>
      29             : #include    <list>
      30             : #include    <memory>
      31             : 
      32             : 
      33             : // C lib includes
      34             : //
      35             : #include    <dirent.h>
      36             : #include    <string.h>
      37             : #include    <unistd.h>
      38             : 
      39             : 
      40             : /** \file
      41             :  * \brief Implementation of a test of the files inherited.
      42             :  *
      43             :  * Most processes are expected to only inherit stdin, stdout, and stderr.
      44             :  * This mimplementation verifies that this is the case.
      45             :  */
      46             : 
      47             : 
      48             : 
      49             : namespace libexcept
      50             : {
      51             : 
      52             : 
      53             : 
      54             : /** \brief Load the command line of the specified process.
      55             :  *
      56             :  * This function loads the cmdline file of the specified process. If an
      57             :  * error occurs, the function returns an empty string. Some processes do
      58             :  * not have a command line.
      59             :  *
      60             :  * If your program can include cppprocess (see eventdispatcher), then I
      61             :  * suggest you use the cppprocess::process_info class instead. It is
      62             :  * much more powerful.
      63             :  *
      64             :  * \param[in] pid  The process identifier.
      65             :  *
      66             :  * \return The command line of the \p pid process.
      67             :  */
      68           1 : std::string get_command_line(pid_t pid)
      69             : {
      70           2 :     std::string filename("/proc/");
      71           1 :     filename += std::to_string(pid);
      72           1 :     filename += "/cmdline";
      73           2 :     std::ifstream cmdline(filename);
      74           1 :     std::string line;
      75           1 :     std::getline(cmdline, line, '\0');
      76           2 :     return line;
      77             : }
      78             : 
      79             : /** \brief Check the list of files opened in this process.
      80             :  *
      81             :  * This function reads the /proc/<pid>/fd directory. If it finds files other
      82             :  * than 0, 1, 2, then it generates an error.
      83             :  *
      84             :  * In Debug mode, the error is fatal (it throws).
      85             :  *
      86             :  * In Release mode, the error is _just_ a warning so the process still starts
      87             :  * but you still get a message letting you know there may be something fishy
      88             :  * going on.
      89             :  *
      90             :  * In case your application actually accepts additional streams, you can
      91             :  * add them to the \p allowed set of file descriptors.
      92             :  *
      93             :  * \param[in] allowed  Additional allowed input streams.
      94             :  */
      95           0 : void verify_inherited_files(allowed_fds_t allowed)
      96             : {
      97           0 :     auto closedir = [](DIR * d)
      98             :     {
      99           0 :         ::closedir(d);
     100           0 :     };
     101             : 
     102           0 :     int errcnt(0);
     103             : 
     104           0 :     std::string path("/proc/");
     105           0 :     path += std::to_string(getpid());
     106           0 :     path += "/fd";
     107           0 :     DIR * d(opendir(path.c_str()));
     108           0 :     std::unique_ptr<DIR, decltype(closedir)> auto_close(d, closedir);
     109             : 
     110             :     for(;;)
     111             :     {
     112           0 :         dirent const * ent(readdir(d));
     113           0 :         if(ent == nullptr)
     114             :         {
     115           0 :             break;
     116             :         }
     117             : 
     118           0 :         char const * basename(ent->d_name);
     119           0 :         if(basename[0] == '.')
     120             :         {
     121             :             // ignore all hidden files
     122           0 :             continue;
     123             :         }
     124             : 
     125           0 :         if(basename[0] >= '0'
     126           0 :         && basename[0] <= '2'
     127           0 :         && basename[1] == '\0')
     128             :         {
     129             :             // skip stdin, stdout, stderr
     130           0 :             continue;
     131             :         }
     132             : 
     133           0 :         int const fd(std::atoi(basename));
     134           0 :         if(fd == dirfd(d))
     135             :         {
     136             :             // ignore the opendir() file descriptor
     137             :             // (it is currently open since we are reading the directory)
     138             :             //
     139           0 :             continue;
     140             :         }
     141             : 
     142           0 :         if(allowed.find(fd) == allowed.end())
     143             :         {
     144           0 :             char link[256];
     145           0 :             ssize_t const l(readlink((path + '/' + basename).c_str(), link, sizeof(link) - 1));
     146           0 :             if(l <= 0)
     147             :             {
     148           0 :                 link[0] = '\0';
     149             :             }
     150             :             else
     151             :             {
     152           0 :                 link[l] = '\0';
     153             :             }
     154             :             std::cerr
     155           0 :                 << "warning: file descriptor "
     156             :                 << fd
     157             :                 << " ("
     158             :                 << link
     159           0 :                 << ") leaked on invocation. Parent PID "
     160             :                 << getppid()
     161             :                 << ": "
     162           0 :                 << get_command_line(getppid())
     163           0 :                 << '\n';
     164           0 :             ++errcnt;
     165             :         }
     166           0 :     }
     167             : 
     168             : #ifdef _DEBUG
     169           0 :     if(errcnt > 0)
     170             :     {
     171             :         // this is a logic error
     172             :         //
     173           0 :         throw file_inherited("found unexpected file descriptor leaks.");
     174             :     }
     175             : #endif
     176           0 : }
     177             : 
     178             : 
     179             : 
     180           6 : }
     181             : // namespace libexcept
     182             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13