libexplain  1.4.D001
libexplain/buffer/errno/shmat.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2011-2013 Peter Miller
00004  *
00005  * This program is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License as published by
00007  * the Free Software Foundation; either version 3 of the License, or (at
00008  * your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful, but
00011  * WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
00013  * General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include <libexplain/ac/errno.h>
00020 #include <libexplain/ac/stdlib.h>
00021 #include <libexplain/ac/string.h>
00022 #include <libexplain/ac/sys/mman.h>
00023 #include <libexplain/ac/sys/shm.h>
00024 #include <libexplain/ac/unistd.h>
00025 
00026 #include <libexplain/buffer/eacces.h>
00027 #include <libexplain/buffer/einval.h>
00028 #include <libexplain/buffer/enomem.h>
00029 #include <libexplain/buffer/errno/generic.h>
00030 #include <libexplain/buffer/errno/shmat.h>
00031 #include <libexplain/buffer/gettext.h>
00032 #include <libexplain/buffer/int.h>
00033 #include <libexplain/buffer/is_the_null_pointer.h>
00034 #include <libexplain/buffer/must_be_multiple_of_page_size.h>
00035 #include <libexplain/buffer/pointer.h>
00036 #include <libexplain/buffer/shmflg.h>
00037 #include <libexplain/explanation.h>
00038 #include <libexplain/getpagesize.h>
00039 #include <libexplain/translate.h>
00040 
00041 
00042 static void
00043 explain_buffer_errno_shmat_system_call(explain_string_buffer_t *sb, int errnum,
00044     int shmid, const void *shmaddr, int shmflg)
00045 {
00046     (void)errnum;
00047     explain_string_buffer_puts(sb, "shmat(shmid = ");
00048     explain_buffer_int(sb, shmid);
00049     explain_string_buffer_puts(sb, ", shmaddr = ");
00050     explain_buffer_pointer(sb, shmaddr);
00051     explain_string_buffer_puts(sb, ", shmflg = ");
00052     explain_buffer_shmflg(sb, shmflg);
00053     explain_string_buffer_putc(sb, ')');
00054 }
00055 
00056 #ifdef HAVE_MINCORE
00057 
00058 static int
00059 memory_already_mapped(const void *data, size_t data_size)
00060 {
00061     int             page_size;
00062     unsigned char   *vec;
00063 
00064     page_size = explain_getpagesize();
00065     if (page_size < 0)
00066         return 0;
00067     vec = malloc((data_size + page_size - 1) / page_size);
00068     if (!vec)
00069         return 0;
00070     /*
00071      * The signed-ness of 'vec' differs by system.  There is a 50/50
00072      * chance you will get a compiler warning on the next line.
00073      */
00074     if (mincore((void *)data, data_size, vec) < 0)
00075     {
00076         free(vec);
00077         /*
00078          * Strictly speaking, only ENOMEM indicates that one or more
00079          * pages were not mapped.
00080          */
00081         return 0;
00082     }
00083     free(vec);
00084     return 1;
00085 }
00086 
00087 #endif
00088 
00089 static int
00090 shmid_exists(int shmid)
00091 {
00092     struct shmid_ds shm;
00093 
00094     return
00095         (
00096             shmctl(shmid, IPC_STAT, &shm) >= 0
00097         ||
00098             errno == EACCES
00099         ||
00100             errno == EPERM
00101         );
00102 }
00103 
00104 
00105 static int
00106 wrap_shmctl(int shmid, int cmd, struct shmid_ds *data)
00107 {
00108     if (shmctl(shmid, cmd, data) >= 0)
00109         return 0;
00110     if (errno == EACCES || errno == EPERM)
00111     {
00112         /*
00113          * Note that shmctl(IPC_STAT) requires read permission, so if we
00114          * get EACCES that means we know we have no read permission, but
00115          * we don't know anything else.  Fake it.
00116          */
00117         memset(data, 0, sizeof(*data));
00118         return 0;
00119     }
00120     return -1;
00121 }
00122 
00123 
00124 void
00125 explain_buffer_errno_shmat_explanation(explain_string_buffer_t *sb, int errnum,
00126     const char *syscall_name, int shmid, const void *shmaddr, int shmflg)
00127 {
00128     /*
00129      * http://www.opengroup.org/onlinepubs/009695399/functions/shmat.html
00130      */
00131     switch (errnum)
00132     {
00133     case EACCES:
00134     case EPERM:
00135         /*
00136          * The calling process does not have the required permissions for
00137          * the requested attach type, and does not have the CAP_IPC_OWNER
00138          * capability.
00139          */
00140         {
00141             struct shmid_ds shm;
00142 
00143             if (wrap_shmctl(shmid, IPC_STAT, &shm) >= 0)
00144             {
00145                 int             read_only;
00146 
00147                 read_only = !!(shmflg & SHM_RDONLY);
00148                 if (explain_buffer_eacces_shmat(sb, &shm.shm_perm, read_only))
00149                     return;
00150             }
00151         }
00152 
00153         /*
00154          * We will have to settle for being vague.
00155          */
00156         explain_buffer_eacces_shmat_vague(sb);
00157         break;
00158 
00159     case EINVAL:
00160 #ifdef SHM_REMAP
00161         if ((shmflg & SHM_REMAP) && !shmaddr)
00162         {
00163             explain_buffer_is_the_null_pointer(sb, "shmaddr");
00164             return;
00165         }
00166 #endif
00167         /* use shmctl to find this out */
00168         if (!shmid_exists(shmid))
00169         {
00170             explain_string_buffer_printf_gettext
00171             (
00172                 sb,
00173                 /*
00174                  * xgettext: This error message is used when explaining that a
00175                  * IPC object does not exist, when it should.
00176                  */
00177                 i18n("the %s does not exist"),
00178                 explain_translate_shared_memory_segment()
00179             );
00180             return;
00181         }
00182 #ifdef HAVE_GETPAGESIZE
00183         if (shmaddr && !(shmflg & SHM_RND))
00184         {
00185             int             page_size;
00186 
00187             page_size = explain_getpagesize();
00188             if
00189             (
00190                 page_size > 0
00191             &&
00192                 ((size_t)shmaddr % (size_t)page_size) != 0
00193             )
00194             {
00195                 explain_buffer_must_be_multiple_of_page_size(sb, "shmaddr");
00196                 return;
00197             }
00198         }
00199 #endif
00200 #ifdef HAVE_MINCORE
00201         {
00202             struct shmid_ds shm;
00203 
00204             /*
00205              * The shmctl system call requires that the caller must have
00206              * read permission on the shared memory segment, so we may
00207              * not be able to be helpful if they aren't the owner (but
00208              * the we shouldn't get to here, that would be EACCES).
00209              */
00210             if
00211             (
00212                 shmctl(shmid, IPC_STAT, &shm) >= 0
00213             &&
00214                 memory_already_mapped(shmaddr, shm.shm_segsz)
00215             )
00216             {
00217                 explain_string_buffer_printf
00218                 (
00219                     sb,
00220                     /* FIXME: i18n */
00221                     "virtual memory has already been mapped at the "
00222                         "address indicated by the %s argument",
00223                     "shmaddr"
00224                 );
00225                 return;
00226             }
00227         }
00228 #endif
00229 
00230         /* Nothing obvious... */
00231         goto generic;
00232 
00233     case ENOMEM: /* Linux */
00234     case EMFILE: /* POSIX */
00235         /*
00236          * See if the process's maximum number of mappings would have
00237          * been exceeded.
00238          */
00239         {
00240             struct shmid_ds shm;
00241 
00242             /*
00243              * The shmctl system call required that the caller must have
00244              * read permission on the shared memory segment.
00245              */
00246             if
00247             (
00248                 shmctl(shmid, IPC_STAT, &shm) >= 0
00249             &&
00250                 explain_buffer_enomem_rlimit_exceeded(sb, shm.shm_segsz)
00251             )
00252             {
00253                 return;
00254             }
00255         }
00256 
00257         /*
00258          * Could not allocate memory
00259          * (a) for the descriptor, or
00260          * (b) for the page tables.
00261          */
00262         explain_buffer_enomem_kernel(sb);
00263         break;
00264 
00265     default:
00266         generic:
00267         explain_buffer_errno_generic(sb, errnum, syscall_name);
00268         break;
00269     }
00270 }
00271 
00272 
00273 void
00274 explain_buffer_errno_shmat(explain_string_buffer_t *sb, int errnum, int shmid,
00275     const void *shmaddr, int shmflg)
00276 {
00277     explain_explanation_t exp;
00278 
00279     explain_explanation_init(&exp, errnum);
00280     explain_buffer_errno_shmat_system_call(&exp.system_call_sb, errnum, shmid,
00281         shmaddr, shmflg);
00282     explain_buffer_errno_shmat_explanation(&exp.explanation_sb, errnum, "shmat",
00283         shmid, shmaddr, shmflg);
00284     explain_explanation_assemble(&exp, sb);
00285 }
00286 
00287 
00288 /* vim: set ts=8 sw=4 et : */