libexplain  1.4.D001
libexplain/fileinfo/pid_exe.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - a library of system-call-specific strerror replacements
00003  * Copyright (C) 2011, 2013 Peter Miller
00004  * Written by Peter Miller <pmiller@opensource.org.au>
00005  *
00006  * This program is free software; you can redistribute it and/or modify it
00007  * under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 3 of the License, or (at your
00009  * option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00014  * License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include <libexplain/ac/fcntl.h>
00021 #include <libexplain/ac/limits.h> /* for PATH_MAX except Solaris */
00022 #include <libexplain/ac/stdio.h>
00023 #include <libexplain/ac/string.h>
00024 #include <libexplain/ac/sys/param.h> /* for PATH_MAX except Solaris */
00025 #include <libexplain/ac/unistd.h>
00026 
00027 #include <libexplain/fileinfo.h>
00028 #include <libexplain/lsof.h>
00029 
00030 
00031 static int
00032 proc_pid_exe(pid_t pid, char *data, size_t data_size)
00033 {
00034 #ifdef PROC_PID_CMDLINE
00035     {
00036         int             fd;
00037         char            path[100];
00038 
00039         snprintf(path, sizeof(path), "/proc/%ld/cmdline", (long)pid);
00040         fd = open(path, O_RDONLY);
00041         if (fd >= 0)
00042         {
00043             ssize_t         n;
00044 
00045             n = read(fd, data, data_size);
00046             close(fd);
00047             if (n > 0)
00048             {
00049                 char            *cp;
00050 
00051                 /*
00052                  * The data consists of each command line argument, and each is
00053                  * terminated by a NUL character, not a space.  Since we only
00054                  * care about the first one (argv[0], the command name) we just
00055                  * proceed as if there was only the one C string present.
00056                  */
00057                 cp = memchr(data, '\0', n);
00058                 if (cp)
00059                 {
00060                     return 1;
00061                 }
00062             }
00063         }
00064     }
00065 #endif
00066 #if defined(PROC_PID_PATH_A_OUT)
00067     if (data_size >= 2)
00068     {
00069         int             n;
00070         char            path[100];
00071 
00072         snprintf(path, sizeof(path), "/proc/%ld/path/a.out", (long)pid);
00073         n = readlink(path, data, data_size - 1);
00074         if (n > 0 && data[0] == '/')
00075         {
00076             data[n] = '\0';
00077             return 1;
00078         }
00079     }
00080 #elif defined(PROC_PID_EXE)
00081     if (data_size >= 2)
00082     {
00083         int             n;
00084         char            path[100];
00085 
00086         snprintf(path, sizeof(path), "/proc/%ld/exe", (long)pid);
00087         n = readlink(path, data, data_size - 1);
00088         if (n > 0 && data[0] == '/')
00089         {
00090             data[n] = '\0';
00091             return 1;
00092         }
00093     }
00094 #else
00095     (void)pid;
00096     (void)data;
00097     (void)data_size;
00098 #endif
00099 
00100     /*
00101      * Sorry, can't help you.
00102      */
00103     return 0;
00104 }
00105 
00106 
00107 typedef struct adapter adapter;
00108 struct adapter
00109 {
00110     explain_lsof_t  inherited;
00111     char            *data;
00112     size_t          data_size;
00113     int             count;
00114 };
00115 
00116 
00117 static void
00118 n_callback(explain_lsof_t *context, const char *name)
00119 {
00120     if (context->fildes == LIBEXPLAIN_LSOF_FD_txt)
00121     {
00122         /*
00123          * Sometimes lsof(1) is less than helpful, and says "exe (readlink:
00124          * Permission denied)" which is effectively no answer at all.
00125          */
00126         if (0 == strstr(name, "exe (readlink:"))
00127         {
00128             adapter         *a;
00129 
00130             a = (adapter *)context;
00131             if (a->count == 0)
00132             {
00133                 explain_strendcpy(a->data, name, a->data + a->data_size);
00134                 a->count++;
00135             }
00136         }
00137     }
00138 }
00139 
00140 
00141 int
00142 explain_fileinfo_pid_exe(pid_t pid, char *data, size_t data_size)
00143 {
00144     /*
00145      * First, try to do it the easy way, through /proc
00146      */
00147     if (proc_pid_exe(pid, data, data_size))
00148     {
00149         return 1;
00150     }
00151 
00152     /*
00153      * It is possible that /proc didn't work.  Maybe ./configure
00154      * was fooled, or it could be that /proc worked in the build
00155      * environment, but it isn't available or doesn't work in the
00156      * runtime environment (e.g. chroot jails).
00157      *
00158      * Try lsof(1) instead.
00159      */
00160     {
00161         adapter         obj;
00162         char            options[40];
00163 
00164         obj.inherited.n_callback = n_callback;
00165         obj.data = data;
00166         obj.data_size = data_size;
00167         obj.count = 0;
00168         snprintf(options, sizeof(options), "-p %ld", (long)pid);
00169         explain_lsof(options, &obj.inherited);
00170         if (obj.count > 0)
00171             return 1;
00172     }
00173 
00174     /*
00175      * Sorry, can't help you.
00176      */
00177     return 0;
00178 }
00179 
00180 
00181 /* vim: set ts=8 sw=4 et : */