LCOV - code coverage report
Current view: top level - src/plugins/wresolver - wresolver.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 113 146 77.4 %
Date: 2019-09-12 12:28:41 Functions: 11 14 78.6 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #ifndef HAVE_KDBCONFIG
      10             : #include "kdbconfig.h"
      11             : #endif
      12             : 
      13             : #include <kdbproposal.h>
      14             : 
      15             : #include <kdbassert.h>
      16             : #include <kdberrors.h>
      17             : 
      18             : #include <string.h>
      19             : 
      20             : #include "wresolver.h"
      21             : #include <errno.h> /* errno in getcwd() */
      22             : #include <stdlib.h>
      23             : #include <sys/stat.h> /* mkdir() */
      24             : #include <unistd.h>   /* getcwd() */
      25             : 
      26             : #if defined(_WIN32)
      27             : #include <io.h>
      28             : #include <shlobj.h>
      29             : #include <windows.h>
      30             : #endif
      31             : 
      32             : /**
      33             :  * @retval 1 on success (Relative path)
      34             :  * @retval 0 on success (Absolute path)
      35             :  * @retval never -1 (success guaranteed)
      36             :  */
      37          20 : int elektraWresolverCheckFile (const char * filename)
      38             : {
      39          20 :         if (filename[0] == '/') return 0;
      40             : 
      41          10 :         return 1;
      42             : }
      43             : 
      44             : typedef struct _resolverHandle resolverHandle;
      45             : 
      46             : struct _resolverHandle
      47             : {
      48             :         time_t mtime; ///< Previous timestamp of the file
      49             :         mode_t mode;  ///< The mode to set
      50             :         int state;    ///< 0 .. invalid -> kdbGet -> 1 .. ready to set -> kdbSet -> 2
      51             : 
      52             :         char * filename; ///< the full path to the configuration file
      53             : 
      54             :         const char * path;
      55             : };
      56             : 
      57             : typedef struct _resolverHandles resolverHandles;
      58             : 
      59             : struct _resolverHandles
      60             : {
      61             :         resolverHandle spec;
      62             :         resolverHandle dir;
      63             :         resolverHandle user;
      64             :         resolverHandle system;
      65             : };
      66             : 
      67          10 : static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey)
      68             : {
      69          10 :         resolverHandles * pks = elektraPluginGetData (handle);
      70          10 :         switch (keyGetNamespace (parentKey))
      71             :         {
      72             :         case KEY_NS_SPEC:
      73           2 :                 return &pks->spec;
      74             :         case KEY_NS_DIR:
      75           4 :                 return &pks->dir;
      76             :         case KEY_NS_USER:
      77           2 :                 return &pks->user;
      78             :         case KEY_NS_SYSTEM:
      79           2 :                 return &pks->system;
      80             :         case KEY_NS_PROC:
      81             :         case KEY_NS_EMPTY:
      82             :         case KEY_NS_NONE:
      83             :         case KEY_NS_META:
      84             :         case KEY_NS_CASCADING:
      85             :                 break;
      86             :         }
      87           0 :         ELEKTRA_ASSERT (0, "namespace %d not valid for resolving", keyGetNamespace (parentKey));
      88             :         return 0;
      89             : }
      90             : 
      91             : 
      92             : static void resolverClose (resolverHandle * p)
      93             : {
      94         960 :         elektraFree (p->filename);
      95         960 :         p->filename = 0;
      96             : }
      97             : 
      98             : static void resolverInit (resolverHandle * p, const char * path)
      99             : {
     100         960 :         p->mtime = 0;
     101         960 :         p->mode = 0;
     102         960 :         p->state = 0;
     103             : 
     104         960 :         p->filename = 0;
     105             : 
     106         960 :         p->path = path;
     107             : }
     108             : 
     109         176 : static void escapePath (char * home)
     110             : {
     111         176 :         int len = strlen (home), i;
     112         528 :         for (i = 0; i < len; ++i)
     113             :         {
     114         352 :                 if (home[i] == '\\')
     115             :                 {
     116           0 :                         home[i] = '/';
     117             :                 }
     118             :         }
     119         176 : }
     120             : 
     121         240 : static void elektraResolveSpec (resolverHandle * p, Key * errorKey)
     122             : {
     123         240 :         char * system = getenv ("ALLUSERSPROFILE");
     124             : 
     125         240 :         if (!system)
     126             :         {
     127         152 :                 system = "";
     128         152 :                 ELEKTRA_ADD_INSTALLATION_WARNING (
     129             :                         errorKey, "Could not retrieve from passwd using getpwuid_r. Could not get ALLUSERSPROFILE for spec, using /");
     130             :         }
     131             :         else
     132             :         {
     133          88 :                 escapePath (system);
     134             :         }
     135             : 
     136             : 
     137         240 :         if (p->path[0] == '/')
     138             :         {
     139             :                 /* Use absolute path */
     140          20 :                 size_t filenameSize = strlen (system) + strlen (p->path) + 1;
     141          20 :                 p->filename = elektraMalloc (filenameSize);
     142          20 :                 strcpy (p->filename, system);
     143          20 :                 strcat (p->filename, p->path);
     144             :                 return;
     145             :         }
     146         220 :         size_t filenameSize = sizeof (KDB_DB_SPEC) + strlen (system) + strlen (p->path) + sizeof ("/") + 1;
     147         220 :         p->filename = elektraMalloc (filenameSize);
     148         220 :         strcpy (p->filename, system);
     149         220 :         strcat (p->filename, KDB_DB_SPEC);
     150         220 :         strcat (p->filename, "/");
     151         220 :         strcat (p->filename, p->path);
     152             :         return;
     153             : }
     154             : 
     155         240 : static void elektraResolveDir (resolverHandle * p, Key * warningsKey)
     156             : {
     157         240 :         p->filename = elektraMalloc (KDB_MAX_PATH_LENGTH);
     158             : 
     159             : #if defined(_WIN32)
     160             :         CHAR dir[MAX_PATH];
     161             :         DWORD dwRet = GetCurrentDirectory (MAX_PATH, dir);
     162             :         if (dwRet == 0)
     163             :         {
     164             :                 char buf[256];
     165             :                 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256,
     166             :                                NULL);
     167             :                 ELEKTRA_ADD_RESOURCE_WARNINGF (warningsKey, "GetCurrentDirectory failed: %s, defaulting to /", buf);
     168             :                 dir[0] = 0;
     169             :         }
     170             :         else if (dwRet > MAX_PATH)
     171             :         {
     172             :                 // TODO: Solution? Compile with higher MAX_PATH or submit a bug to the bug tracker and let us do it for you
     173             :                 ELEKTRA_ADD_INSTALLATION_WARNINGF (warningsKey, "GetCurrentDirectory failed, buffer size too small, needed: %ld", dwRet);
     174             :                 dir[0] = 0;
     175             :         }
     176             :         escapePath (dir);
     177             : #else
     178             :         char dir[KDB_MAX_PATH_LENGTH];
     179         240 :         if (getcwd (dir, KDB_MAX_PATH_LENGTH) == 0)
     180             :         {
     181           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (warningsKey, "Command 'getcwd' failed. Defaulting to /. Reason: %s", strerror (errno));
     182           0 :                 dir[0] = 0;
     183             :         }
     184             : #endif
     185             : 
     186         240 :         strcpy (p->filename, dir);
     187         240 :         strcat (p->filename, "/");
     188         240 :         strncat (p->filename, p->path, KDB_MAX_PATH_LENGTH - strlen (dir) - 3);
     189         240 :         p->filename[KDB_MAX_PATH_LENGTH - 1] = 0;
     190             : 
     191         240 :         return;
     192             : }
     193             : 
     194         240 : static void elektraResolveUser (resolverHandle * p, Key * warningsKey)
     195             : {
     196         240 :         p->filename = elektraMalloc (KDB_MAX_PATH_LENGTH);
     197             : 
     198             : #if defined(_WIN32)
     199             :         CHAR home[MAX_PATH];
     200             :         if (SUCCEEDED (SHGetFolderPath (NULL, CSIDL_PROFILE, NULL, 0, home)))
     201             :         {
     202             :                 escapePath (home);
     203             :         }
     204             :         else
     205             :         {
     206             :                 strcpy (home, "");
     207             :                 ELEKTRA_ADD_INSTALLATION_WARNING (warningsKey, "Could not get home (CSIDL_PROFILE), using /");
     208             :         }
     209             : #else
     210         240 :         char * home = (char *) getenv ("HOME");
     211         240 :         if (!home)
     212             :         {
     213         176 :                 home = "";
     214         176 :                 ELEKTRA_ADD_INSTALLATION_WARNING (warningsKey, "Could not get HOME environment variable, using /");
     215             :         }
     216             : #endif
     217             : 
     218         240 :         strcpy (p->filename, home);
     219         240 :         strcat (p->filename, "/");
     220         240 :         strncat (p->filename, p->path, KDB_MAX_PATH_LENGTH);
     221         240 : }
     222             : 
     223         240 : static void elektraResolveSystem (resolverHandle * p, Key * errorKey)
     224             : {
     225         240 :         char * system = getenv ("ALLUSERSPROFILE");
     226             : 
     227         240 :         if (!system)
     228             :         {
     229         152 :                 system = "";
     230         152 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Could not get ALLUSERSPROFILE environment variable, using /");
     231             :         }
     232             :         else
     233             :         {
     234          88 :                 escapePath (system);
     235             :         }
     236             : 
     237         240 :         if (p->path[0] == '/')
     238             :         {
     239             :                 /* Use absolute path */
     240          20 :                 size_t filenameSize = strlen (system) + strlen (p->path) + 1;
     241          20 :                 p->filename = elektraMalloc (filenameSize);
     242          20 :                 strcpy (p->filename, system);
     243          20 :                 strcat (p->filename, p->path);
     244             :                 return;
     245             :         }
     246         220 :         size_t filenameSize = sizeof (KDB_DB_SYSTEM) + strlen (system) + strlen (p->path) + sizeof ("/") + 1;
     247         220 :         p->filename = elektraMalloc (filenameSize);
     248         220 :         strcpy (p->filename, system);
     249         220 :         strcat (p->filename, KDB_DB_SYSTEM);
     250         220 :         strcat (p->filename, "/");
     251         220 :         strcat (p->filename, p->path);
     252             :         return;
     253             : }
     254             : 
     255         240 : int elektraWresolverOpen (Plugin * handle, Key * errorKey)
     256             : {
     257         240 :         KeySet * resolverConfig = elektraPluginGetConfig (handle);
     258         240 :         const char * path = keyString (ksLookupByName (resolverConfig, "/path", 0));
     259             : 
     260         240 :         if (!path)
     261             :         {
     262           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Could not find file configuration");
     263           0 :                 return -1;
     264             :         }
     265             : 
     266         240 :         resolverHandles * p = elektraMalloc (sizeof (resolverHandles));
     267             : 
     268             :         // switch is only present to forget no namespace and to get
     269             :         // a warning whenever a new namespace is present.
     270             :         // (also used below in close)
     271             :         // In fact its linear code executed:
     272             :         switch (KEY_NS_SPEC)
     273             :         {
     274             :         case KEY_NS_SPEC:
     275         480 :                 resolverInit (&p->spec, path);
     276         240 :                 elektraResolveSpec (&p->spec, errorKey);
     277             :         // FALLTHROUGH
     278             :         case KEY_NS_DIR:
     279         480 :                 resolverInit (&p->dir, path);
     280         240 :                 elektraResolveDir (&p->dir, errorKey);
     281             :         // FALLTHROUGH
     282             :         case KEY_NS_USER:
     283         480 :                 resolverInit (&p->user, path);
     284         240 :                 elektraResolveUser (&p->user, errorKey);
     285             :         // FALLTHROUGH
     286             :         case KEY_NS_SYSTEM:
     287         480 :                 resolverInit (&p->system, path);
     288         240 :                 elektraResolveSystem (&p->system, errorKey);
     289             :         // FALLTHROUGH
     290             :         case KEY_NS_PROC:
     291             :         case KEY_NS_EMPTY:
     292             :         case KEY_NS_NONE:
     293             :         case KEY_NS_META:
     294             :         case KEY_NS_CASCADING:
     295             :                 break;
     296             :         }
     297             : 
     298         240 :         elektraPluginSetData (handle, p);
     299             : 
     300         240 :         return 0; /* success */
     301             : }
     302             : 
     303         240 : int elektraWresolverClose (Plugin * handle, Key * errorKey ELEKTRA_UNUSED)
     304             : {
     305         240 :         resolverHandles * ps = elektraPluginGetData (handle);
     306             : 
     307         240 :         if (ps)
     308             :         {
     309             :                 switch (KEY_NS_SPEC)
     310             :                 {
     311             :                 case KEY_NS_SPEC:
     312         240 :                         resolverClose (&ps->spec); // FALLTHROUGH
     313             :                 case KEY_NS_DIR:
     314         240 :                         resolverClose (&ps->dir); // FALLTHROUGH
     315             :                 case KEY_NS_USER:
     316         240 :                         resolverClose (&ps->user); // FALLTHROUGH
     317             :                 case KEY_NS_SYSTEM:
     318         240 :                         resolverClose (&ps->system); // FALLTHROUGH
     319             :                 case KEY_NS_PROC:
     320             :                 case KEY_NS_EMPTY:
     321             :                 case KEY_NS_NONE:
     322             :                 case KEY_NS_META:
     323             :                 case KEY_NS_CASCADING:
     324             :                         break;
     325             :                 }
     326             : 
     327         240 :                 elektraFree (ps);
     328         240 :                 elektraPluginSetData (handle, 0);
     329             :         }
     330             : 
     331         240 :         return 0; /* success */
     332             : }
     333             : 
     334         170 : int elektraWresolverGet (Plugin * handle, KeySet * returned, Key * parentKey)
     335             : {
     336         170 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/wresolver"))
     337             :         {
     338         160 :                 KeySet * contract = ksNew (
     339             :                         30, keyNew ("system/elektra/modules/wresolver", KEY_VALUE, "wresolver plugin waits for your orders", KEY_END),
     340             :                         keyNew ("system/elektra/modules/wresolver/exports", KEY_END),
     341             :                         keyNew ("system/elektra/modules/wresolver/exports/open", KEY_FUNC, elektraWresolverOpen, KEY_END),
     342             :                         keyNew ("system/elektra/modules/wresolver/exports/close", KEY_FUNC, elektraWresolverClose, KEY_END),
     343             :                         keyNew ("system/elektra/modules/wresolver/exports/get", KEY_FUNC, elektraWresolverGet, KEY_END),
     344             :                         keyNew ("system/elektra/modules/wresolver/exports/set", KEY_FUNC, elektraWresolverSet, KEY_END),
     345             :                         keyNew ("system/elektra/modules/wresolver/exports/error", KEY_FUNC, elektraWresolverError, KEY_END),
     346             :                         keyNew ("system/elektra/modules/wresolver/exports/checkfile", KEY_FUNC, elektraWresolverCheckFile, KEY_END),
     347             : #include ELEKTRA_README
     348             :                         keyNew ("system/elektra/modules/wresolver/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     349         160 :                 ksAppend (returned, contract);
     350         160 :                 ksDel (contract);
     351             : 
     352         160 :                 return 1; /* success */
     353             :         }
     354             :         /* get all keys */
     355             : 
     356          10 :         resolverHandle * pk = elektraGetResolverHandle (handle, parentKey);
     357          10 :         keySetString (parentKey, pk->filename);
     358             : 
     359          10 :         pk->state = 1;
     360             : 
     361             :         struct stat buf;
     362             : 
     363          20 :         if (stat (pk->filename, &buf) == -1)
     364             :         {
     365             :                 // no file, so storage has no job
     366          10 :                 pk->mtime = 0; // no file, so no time
     367          10 :                 return 0;
     368             :         }
     369             : 
     370             :         /* Check if update needed */
     371           0 :         if (pk->mtime == buf.st_mtime)
     372             :         {
     373             :                 // no update, so storage has no job
     374             :                 return 0;
     375             :         }
     376             : 
     377           0 :         pk->mtime = buf.st_mtime;
     378             : 
     379           0 :         return 1; /* success */
     380             : }
     381             : 
     382           0 : int elektraWresolverSet (Plugin * handle, KeySet * returned ELEKTRA_UNUSED, Key * parentKey)
     383             : {
     384           0 :         resolverHandle * pk = elektraGetResolverHandle (handle, parentKey);
     385           0 :         keySetString (parentKey, pk->filename);
     386             : 
     387           0 :         switch (pk->state)
     388             :         {
     389             :         case 0:
     390           0 :                 ELEKTRA_SET_CONFLICTING_STATE_ERROR (parentKey, "Command 'kdbSet()' called before 'kdbGet()'");
     391           0 :                 return -1;
     392             :         case 1:
     393           0 :                 ++pk->state;
     394           0 :                 break;
     395             :         case 2:
     396           0 :                 pk->state = 1;
     397             :                 // nothing to do on commit
     398           0 :                 return 1;
     399             :         }
     400             : 
     401             :         /* set all keys */
     402           0 :         if (pk->mtime == 0)
     403             :         {
     404             :                 // this can happen if the kdbGet() path found no file
     405             : 
     406             :                 // no conflict possible, so just return successfully
     407             :                 return 1;
     408             :         }
     409             : 
     410             :         struct stat buf;
     411             : 
     412           0 :         if (stat (pk->filename, &buf) == -1)
     413             :         {
     414           0 :                 ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Could not stat config file '%s'", pk->filename);
     415             :                 // no file found, nothing to do
     416           0 :                 return 0;
     417             :         }
     418             : 
     419             :         /* Check for conflict */
     420           0 :         if (pk->mtime != buf.st_mtime)
     421             :         {
     422             :                 // conflict
     423           0 :                 ELEKTRA_SET_CONFLICTING_STATE_ERRORF (
     424             :                         parentKey,
     425             :                         "Conflict, file modification time stamp %ld is different than our time stamp %ld config file name is '%s'",
     426             :                         (long) buf.st_mtime, (long) pk->mtime, pk->filename);
     427           0 :                 pk->state = 0; // invalid state, need to kdbGet again
     428           0 :                 return -1;
     429             :         }
     430             : 
     431           0 :         pk->mtime = buf.st_mtime;
     432             : 
     433           0 :         return 1; /* success */
     434             : }
     435             : 
     436           0 : int elektraWresolverError (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     437             : {
     438           0 :         resolverHandle * pk = elektraGetResolverHandle (handle, parentKey);
     439           0 :         pk->state = 1;
     440             : 
     441             : 
     442           0 :         return 1; /* success */
     443             : }
     444             : 
     445         240 : Plugin * ELEKTRA_PLUGIN_EXPORT
     446             : {
     447             :         // clang-format off
     448         240 :         return elektraPluginExport("wresolver",
     449             :                 ELEKTRA_PLUGIN_OPEN,    &elektraWresolverOpen,
     450             :                 ELEKTRA_PLUGIN_CLOSE,   &elektraWresolverClose,
     451             :                 ELEKTRA_PLUGIN_GET,     &elektraWresolverGet,
     452             :                 ELEKTRA_PLUGIN_SET,     &elektraWresolverSet,
     453             :                 ELEKTRA_PLUGIN_ERROR,   &elektraWresolverError,
     454             :                 ELEKTRA_PLUGIN_END);
     455             : }
     456             : 

Generated by: LCOV version 1.13