summaryrefslogtreecommitdiff
path: root/nuttx/binfmt/binfmt_exepath.c
blob: 1e05d55f3bc3c7afb3367be1e64035e33c0b3bc4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/****************************************************************************
 * binfmt/binfmt_exepath.c
 *
 *   Copyright (C) 2012 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <nuttx/kmalloc.h>
#include <nuttx/binfmt/binfmt.h>

#if !defined(CONFIG_BINFMT_DISABLE) && defined(CONFIG_BINFMT_EXEPATH)

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct exepath_s
{
  FAR char *next; /* Pointer to the next (unterminated) value in the PATH variable */
  char path[1];
};
#define SIZEOF_EXEPATH_S(n) (sizeof(struct exepath_s) + (n) - 1)

/****************************************************************************
 * Private Data
 ****************************************************************************/

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: exepath_init
 *
 * Description:
 *   Initialize for the traversal of each value in the PATH variable.  The
 *   usage is sequence is as follows:
 *
 *   1) Call exepath_init() to initialize for the traversal.  exepath_init()
 *      will return an opaque handle that can then be provided to
 *      exepath_next() and exepath_release().
 *   2) Call exepath_next() repeatedly to examine every file that lies
 *      in the directories of the PATH variable
 *   3) Call exepath_release() to free resources set aside by exepath_init().
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   On success, exepath_init() return a non-NULL, opaque handle that may
 *   subsequently be used in calls to exepath_next() and exepath_release().
 *   On error, a NULL handle value will be returned.  The most likely cause
 *   of an error would be that the there is no value associated with the
 *   PATH variable.
 *
 ****************************************************************************/

EXEPATH_HANDLE exepath_init(void)
{
  FAR struct exepath_s *exepath;
  FAR char *path;

  /* Get the value of the PATH variable */

  path = getenv("PATH");
  if (!path)
    {
      /* getenv() will return a NULL value if the PATH variable does not
       * exist in the environment.
       */

      return (EXEPATH_HANDLE)NULL;
    }

  /* Allocate a container for the PATH variable contents */

  exepath = (FAR struct exepath_s *)kmm_malloc(SIZEOF_EXEPATH_S(strlen(path) + 1));
  if (!exepath)
    {
      /* Ooops.. we are out of memory */

      return (EXEPATH_HANDLE)NULL;
    }

  /* Populate the container */

  strcpy(exepath->path, path);
  exepath->next = exepath->path;

  /* And return the containing cast to an opaque handle */

  return (EXEPATH_HANDLE)exepath;
}

 /****************************************************************************
 * Name: exepath_next
 *
 * Description:
 *   Traverse all possible values in the PATH variable in attempt to find
 *   the full path to an executable file when only a relative path is
 *   provided.
 *
 * Input Parameters:
 *   handle - The handle value returned by exepath_init
 *   relpath - The relative path to the file to be found.
 *
 * Returned Value:
 *   On success, a non-NULL pointer to a null-terminated string is provided.
 *   This is the full path to a file that exists in the file system.  This
 *   function will verify that the file exists (but will not verify that it
 *   is marked executable).
 *
 *   NOTE: The string pointer return in the success case points to allocated
 *   memory.  This memory must be freed by the called by calling kmm_free().
 *
 *   NULL is returned if no path is found to any file with the provided
 *   'relpath' from any absolute path in the PATH variable.  In this case,
 *   there is no point in calling exepath_next() further; exepath_release()
 *   must be called to release resources set aside by expath_init().
 *
 ****************************************************************************/

FAR char *exepath_next(EXEPATH_HANDLE handle, FAR const char *relpath)
{
  FAR struct exepath_s *exepath = (FAR struct exepath_s *)handle;
  struct stat buf;
  FAR char *endptr;
  FAR char *path;
  FAR char *fullpath;
  int pathlen;
  int ret;

  /* Verify that a value handle and relative path were provided */

  DEBUGASSERT(exepath && relpath);
  DEBUGASSERT(relpath[0] != '\0' && relpath[0] != '/');

  /* Loop until (1) we find a file with this relative path from one of the
   * absolute paths in the PATH variable, or (2) all of the absolute paths
   * in the PATH variable have been considered.
   */

  for (;;)
    {
      /* Make sure that exepath->next points to the beginning of a string */

      path = exepath->next;
      if (*path == '\0')
       {
          /* If it points to a NULL it means that either (1) the PATH varialbe
           * is empty, or (2) we have already examined all of the paths in the
           * path variable.
           */

          return (FAR char *)NULL;
       }

      /* Okay... 'path' points to the beginning of the string.  The string may
       * be termined either with (1) ':' which separates the path from the
       * next path in the list, or (2) NUL which marks the end of the list.
       */

      endptr = strchr(path, ':');
      if (!endptr)
        {
          /* If strchr returns NUL it means that ':' does not appear in the
           * string.  Therefore, this must be the final path in the PATH
           * variable content.
           */

          endptr = &path[strlen(path)];
          exepath->next = endptr;
          DEBUGASSERT(*endptr == '\0');
        }
      else
        {
          DEBUGASSERT(*endptr == ':');
          exepath->next = endptr + 1;
          *endptr = '\0';
        }

      pathlen  = strlen(path) + strlen(relpath) + 2;
      fullpath = (FAR char *)kmm_malloc(pathlen);
      if (!fullpath)
        {
          /* Failed to allocate memory */

          return (FAR char *)NULL;
        }

      /* Construct the full path */

      sprintf(fullpath, "%s/%s", path, relpath);

      /* Verify that a regular file exists at this path */

      ret = stat(fullpath, &buf);;
      if (ret == OK && S_ISREG(buf.st_mode))
        {
          return fullpath;
        }

      /* Failed to stat the file.  Just free the allocated memory and
       * continue to try the next path.
       */

       kmm_free(fullpath);
    }

  /* We will not get here */
}

/****************************************************************************
 * Name: exepath_release
 *
 * Description:
 *   Release all resources set aside by exepath_init() when the handle value
 *   was created.  The handle value is invalid on return from this function.
 *   Attempts to all exepath_next() or exepath_release() with such a 'stale'
 *   handle will result in undefined (i.e., not good) behavior.
 *
 * Input Parameters:
 *   handle - The handle value returned by exepath_init
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

void exepath_release(EXEPATH_HANDLE handle)
{
  kmm_free(handle);
}

#endif /* !CONFIG_BINFMT_DISABLE && CONFIG_BINFMT_EXEPATH */