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 287
|
/*
* Vector handling (counted lists of char *'s).
*
* A vector is a table for handling a list of strings with less overhead than
* linked list. The intention is for vectors, once allocated, to be reused;
* this saves on memory allocations once the array of char *'s reaches a
* stable size.
*
* This is based on the util/vector.c library, but that library uses xmalloc
* routines to exit the program if memory allocation fails. This is a
* modified version of the vector library that instead returns false on
* failure to allocate memory, allowing the caller to do appropriate recovery.
*
* Vectors require list of strings, not arbitrary binary data, and cannot
* handle data elements containing nul characters.
*
* Only the portions of the vector library used by PAM modules are
* implemented.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
*
* The authors hereby relinquish any claim to any copyright that they may have
* in this work, whether granted under contract or by operation of law or
* international treaty, and hereby commit to the public, at large, that they
* shall not, at any time in the future, seek to enforce any copyright in this
* work against any person or entity, or prevent any person or entity from
* copying, publishing, distributing or creating derivative works of this
* work.
*/
#include <config.h>
#include <portable/system.h>
#include <pam-util/vector.h>
/*
* Allocate a new, empty vector. Returns NULL if memory allocation fails.
*/
struct vector *
vector_new(void)
{
return calloc(1, sizeof(struct vector));
}
/*
* Allocate a new vector that's a copy of an existing vector. Returns NULL if
* memory allocation fails.
*/
struct vector *
vector_copy(const struct vector *old)
{
struct vector *vector;
size_t i;
vector = vector_new();
if (!vector_resize(vector, old->count)) {
vector_free(vector);
return NULL;
}
vector->count = old->count;
for (i = 0; i < old->count; i++) {
vector->strings[i] = strdup(old->strings[i]);
if (vector->strings[i] == NULL) {
vector_free(vector);
return NULL;
}
}
return vector;
}
/*
* Resize a vector (using reallocarray to resize the table). Return false if
* memory allocation fails.
*/
bool
vector_resize(struct vector *vector, size_t size)
{
size_t i;
char **strings;
if (vector->count > size) {
for (i = size; i < vector->count; i++)
free(vector->strings[i]);
vector->count = size;
}
if (size == 0) {
free(vector->strings);
vector->strings = NULL;
} else {
strings = reallocarray(vector->strings, size, sizeof(char *));
if (strings == NULL)
return false;
vector->strings = strings;
}
vector->allocated = size;
return true;
}
/*
* Add a new string to the vector, resizing the vector as necessary. The
* vector is resized an element at a time; if a lot of resizes are expected,
* vector_resize should be called explicitly with a more suitable size.
* Return false if memory allocation fails.
*/
bool
vector_add(struct vector *vector, const char *string)
{
size_t next = vector->count;
if (vector->count == vector->allocated)
if (!vector_resize(vector, vector->allocated + 1))
return false;
vector->strings[next] = strdup(string);
if (vector->strings[next] == NULL)
return false;
vector->count++;
return true;
}
/*
* Empty a vector but keep the allocated memory for the pointer table.
*/
void
vector_clear(struct vector *vector)
{
size_t i;
for (i = 0; i < vector->count; i++)
if (vector->strings[i] != NULL)
free(vector->strings[i]);
vector->count = 0;
}
/*
* Free a vector completely.
*/
void
vector_free(struct vector *vector)
{
if (vector == NULL)
return;
vector_clear(vector);
free(vector->strings);
free(vector);
}
/*
* Given a vector that we may be reusing, clear it out. If the first argument
* is NULL, allocate a new vector. Used by vector_split*. Returns NULL if
* memory allocation fails.
*/
static struct vector *
vector_reuse(struct vector *vector)
{
if (vector == NULL)
return vector_new();
else {
vector_clear(vector);
return vector;
}
}
/*
* Given a string and a set of separators expressed as a string, count the
* number of strings that it will split into when splitting on those
* separators.
*/
static size_t
split_multi_count(const char *string, const char *seps)
{
const char *p;
size_t count;
if (*string == '\0')
return 0;
for (count = 1, p = string + 1; *p != '\0'; p++)
if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
count++;
/*
* If the string ends in separators, we've overestimated the number of
* strings by one.
*/
if (strchr(seps, p[-1]) != NULL)
count--;
return count;
}
/*
* Given a string, split it at any of the provided separators to form a
* vector, copying each string segment. If the third argument isn't NULL,
* reuse that vector; otherwise, allocate a new one. Any number of
* consecutive separators are considered a single separator. Returns NULL on
* memory allocation failure, after which the provided vector may only have
* partial results.
*/
struct vector *
vector_split_multi(const char *string, const char *seps,
struct vector *vector)
{
const char *p, *start;
size_t i, count;
bool created = false;
if (vector == NULL)
created = true;
vector = vector_reuse(vector);
if (vector == NULL)
return NULL;
count = split_multi_count(string, seps);
if (vector->allocated < count && !vector_resize(vector, count))
goto fail;
vector->count = 0;
for (start = string, p = string, i = 0; *p != '\0'; p++)
if (strchr(seps, *p) != NULL) {
if (start != p) {
vector->strings[i] = strndup(start, (size_t) (p - start));
if (vector->strings[i] == NULL)
goto fail;
i++;
vector->count++;
}
start = p + 1;
}
if (start != p) {
vector->strings[i] = strndup(start, (size_t) (p - start));
if (vector->strings[i] == NULL)
goto fail;
vector->count++;
}
return vector;
fail:
if (created)
vector_free(vector);
return NULL;
}
/*
* Given a vector and a path to a program, exec that program with the vector
* as its arguments. This requires adding a NULL terminator to the vector and
* casting it appropriately. Returns 0 on success and -1 on error, like exec
* does.
*/
int
vector_exec(const char *path, struct vector *vector)
{
if (vector->allocated == vector->count)
if (!vector_resize(vector, vector->count + 1))
return -1;
vector->strings[vector->count] = NULL;
return execv(path, (char * const *) vector->strings);
}
/*
* Given a vector, a path to a program, and the environment, exec that program
* with the vector as its arguments and the given environment. This requires
* adding a NULL terminator to the vector and casting it appropriately.
* Returns 0 on success and -1 on error, like exec does.
*/
int
vector_exec_env(const char *path, struct vector *vector,
const char * const env[])
{
if (vector->allocated == vector->count)
if (!vector_resize(vector, vector->count + 1))
return -1;
vector->strings[vector->count] = NULL;
return execve(path, (char * const *) vector->strings,
(char * const *) env);
}
|