dualinit

A meta-init system for linux
Log | Files | Refs | LICENSE

config.c (8381B)


      1 #include "config.h"
      2 
      3 #include "common.h"
      4 #include "mount.h"
      5 
      6 #include <ctype.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <sys/fcntl.h>
     11 #include <sys/mount.h>
     12 #include <unistd.h>
     13 
     14 #define CHECK_SECTION              \
     15 	if (current_section == NULL) { \
     16 		result = P_USAGE;          \
     17 		goto error;                \
     18 	}
     19 
     20 #define CHECK_ROOT                 \
     21 	if (current_section != NULL) { \
     22 		result = P_USAGE;          \
     23 		goto error;                \
     24 	}
     25 
     26 #define CHECK_PARAMS_EQUALS(n) \
     27 	if (columns_size != (n)) { \
     28 		result = P_USAGE;      \
     29 		goto error;            \
     30 	}
     31 
     32 #define CHECK_PARAMS_BETWEEN(a, b)                  \
     33 	if (columns_size < (a) || columns_size > (b)) { \
     34 		result = P_USAGE;                           \
     35 		goto error;                                 \
     36 	}
     37 
     38 #define CHECK_PARAMS_MORE(a)  \
     39 	if (columns_size < (a)) { \
     40 		result = P_USAGE;     \
     41 		goto error;           \
     42 	}
     43 
     44 #define PARSE_BOOL(dest)                   \
     45 	if (streq(columns[1], "true"))         \
     46 		dest = true;                       \
     47 	else if (streq(columns[1], "false")) { \
     48 		dest = false;                      \
     49 	} else {                               \
     50 		result = P_DATA;                   \
     51 		goto error;                        \
     52 	}
     53 
     54 
     55 section_t sections[SECTION_MAX];
     56 mount_t	  mounts[SECTION_MOUNT_MAX];
     57 int		  section_size = 0;
     58 int		  mount_size   = 0;
     59 bool	  color		   = false;
     60 bool	  verbose	   = true;
     61 int		  timeout	   = 10;
     62 
     63 
     64 parse_error_t config_parse(int fd, const char* filename) {
     65 	FILE* file = fdopen(fd, "r");
     66 	return config_parsef(file, filename);
     67 }
     68 
     69 parse_error_t config_parsef(FILE* file, const char* filename) {
     70 	section_t*	  current_section = NULL;
     71 	bool		  in_mount		  = false;
     72 	int			  linenr		  = 0;
     73 	parse_error_t result		  = 0;
     74 	int			  columns_size	  = 0;
     75 	size_t		  alloc			  = 0;
     76 	char*		  line_origin	  = NULL;
     77 	ssize_t		  len			  = 0;
     78 	ssize_t		  last_blank	  = 0;
     79 	char*		  line			  = NULL;
     80 	char		  columns[10][100];
     81 
     82 	// `getline` fetches one line from `file`
     83 	while ((len = getline(&line_origin, &alloc, file)) > 0) {
     84 		linenr++;
     85 		// as `getline` doesn't add a terminating `\0`, concatinate it
     86 		if (len + 1 > (ssize_t) alloc) {
     87 			line = realloc(line_origin, alloc = len + 1);
     88 			if (line == NULL) {
     89 				printf("error: cannot allocate line\n");
     90 				result = P_ALLOC;
     91 				goto error;
     92 			}
     93 			line_origin = line;
     94 		} else {
     95 			line = line_origin;
     96 		}
     97 		line[len] = '\0';
     98 
     99 		last_blank = -1;
    100 
    101 		// some truncating
    102 		while (isblank(line[0]))
    103 			line++, len--;
    104 
    105 		for (ssize_t i = 0; i < len; i++) {
    106 			if (isblank(line[i])) {
    107 				if (last_blank == -1)
    108 					last_blank = i;
    109 			} else if (line[i] == '\n' || line[i] == ';' || line[i] == '#') {
    110 				len = (last_blank != -1) ? last_blank : i;
    111 				break;
    112 			} else {
    113 				last_blank = -1;
    114 			}
    115 		}
    116 
    117 		// if it's an empty string, skip it
    118 		if (len == 0)
    119 			continue;
    120 
    121 		// parse colums (aka. split by string)
    122 		columns_size	  = 0;
    123 		int	 column_index = 0;
    124 		bool string		  = false;
    125 		for (ssize_t i = 0; i <= len; i++) {
    126 			if (i == len || (isblank(line[i]) && !string)) {
    127 				if (column_index == 1 && columns[columns_size][0] == '-') {
    128 					columns[columns_size][0] = '\0';
    129 					columns_size++;
    130 					column_index = 0;
    131 				} else if (column_index > 0) {
    132 					columns[columns_size][column_index] = '\0';
    133 					columns_size++;
    134 					column_index = 0;
    135 				}
    136 			} else if (line[i] == '"') {
    137 				string = !string;
    138 			} else {
    139 				columns[columns_size][column_index++] = line[i];
    140 			}
    141 		}
    142 
    143 		// end
    144 		if (streq(columns[0], "end")) {
    145 			CHECK_PARAMS_EQUALS(1);
    146 
    147 			if (in_mount) {
    148 				in_mount = false;
    149 			} else if (current_section != NULL) {
    150 				current_section = NULL;
    151 			} else {
    152 				result = P_SCOPE;
    153 				goto error;
    154 			}
    155 			// <fstype> <source> <target> [options]
    156 		} else if (in_mount) {
    157 			CHECK_PARAMS_BETWEEN(3, 4);
    158 
    159 			mount_t* mnt = (current_section != NULL)
    160 							 ? &current_section->mounts[current_section->mount_size++]
    161 							 : &mounts[mount_size++];
    162 
    163 			mnt->try	= columns[0][0] == '*';
    164 			mnt->type	= strdupn(mnt->try ? columns[0] + 1 : columns[0]);
    165 			mnt->source = strdupn(columns[1]);
    166 			mnt->target = strdupn(columns[2]);
    167 			if (columns_size == 4) {
    168 				mnt->flags = mount_flags(columns[3], &mnt->options);
    169 			} else {
    170 				mnt->flags	 = 0;
    171 				mnt->options = NULL;
    172 			}
    173 			// mount
    174 		} else if (streq(columns[0], "mount")) {
    175 			CHECK_PARAMS_EQUALS(1);
    176 
    177 			in_mount = true;
    178 			// section
    179 		} else if (streq(columns[0], "section")) {
    180 			CHECK_ROOT;
    181 			CHECK_PARAMS_EQUALS(3);
    182 
    183 			if (current_section != NULL) {
    184 				result = P_REDEF;
    185 				goto error;
    186 			}
    187 
    188 			current_section				= &sections[section_size++];
    189 			current_section->init		= NULL;
    190 			current_section->mount_size = 0;
    191 			current_section->name		= strdupn(columns[1]);
    192 			current_section->root		= strdupn(columns[2]);
    193 			// rshare/share <dirs...>
    194 		} else if (streq(columns[0], "rshare") || streq(columns[0], "share")) {
    195 			CHECK_PARAMS_MORE(2);
    196 
    197 			for (int i = 1; i < columns_size; i++) {
    198 				mount_t* mnt = (current_section != NULL)
    199 								 ? &current_section->mounts[current_section->mount_size++]
    200 								 : &mounts[mount_size++];
    201 
    202 				char* target = columns[i];
    203 
    204 				mnt->try = target[0] == '*';
    205 				if (mnt->try)
    206 					target++;
    207 				mnt->source	 = strdupn(target);
    208 				mnt->target	 = strdupn(target);
    209 				mnt->type	 = NULL;
    210 				mnt->options = NULL;
    211 				mnt->flags	 = MS_BIND;
    212 				if (columns[0][0] == 'r')	 // aka. equals rshare
    213 					mnt->flags |= MS_REC;
    214 			}
    215 			// color <enable>
    216 		} else if (streq(columns[0], "color")) {
    217 			CHECK_ROOT;
    218 			CHECK_PARAMS_EQUALS(2);
    219 
    220 			PARSE_BOOL(color);
    221 			// verbose <enable>
    222 		} else if (streq(columns[0], "verbose")) {
    223 			CHECK_ROOT;
    224 			CHECK_PARAMS_EQUALS(2);
    225 
    226 			PARSE_BOOL(verbose);
    227 			// timeout <seconds>
    228 		} else if (streq(columns[0], "timeout")) {
    229 			CHECK_ROOT;
    230 			CHECK_PARAMS_EQUALS(2);
    231 
    232 			char* end;
    233 			timeout = strtol(columns[1], &end, 10);
    234 			if (end != strchr(columns[1], '\0')) {
    235 				result = P_DATA;
    236 				goto error;
    237 			}
    238 			// init <path>
    239 		} else if (streq(columns[0], "init")) {
    240 			CHECK_SECTION;
    241 			CHECK_PARAMS_MORE(2);
    242 
    243 			if (current_section->init != NULL) {
    244 				result = P_REDEF;
    245 				goto error;
    246 			}
    247 
    248 			current_section->init = strdupn(columns[1]);
    249 			// include <file>
    250 		} else if (streq(columns[0], "include")) {
    251 			CHECK_ROOT;
    252 			CHECK_PARAMS_EQUALS(2);
    253 
    254 			int fd = open(columns[1], O_RDONLY | O_NONBLOCK);
    255 			result = config_parse(fd, columns[1]);
    256 			close(fd);
    257 			if (result != 0)
    258 				goto cleanup;
    259 			// mmpf, unknown command
    260 		} else {
    261 			result = P_IDENTIFIER;
    262 			goto error;
    263 		}
    264 	}
    265 
    266 	// if there were no sections, raise an error as that not the intension
    267 	if (section_size == 0) {
    268 		result = P_SECTION;
    269 		goto error;
    270 	}
    271 
    272 	// if you reach this, there were no errors
    273 	// just skip to `cleanup`
    274 	goto cleanup;
    275 
    276 error:
    277 	// pretty-print error
    278 	printf("error in %s:%d: ", filename, linenr);
    279 	switch (result) {
    280 		case P_ALLOC:
    281 			printf("cannot allocate line\n");
    282 			break;
    283 		case P_SECTION:
    284 			printf("no section defined\n");
    285 			break;
    286 		case P_IDENTIFIER:
    287 			printf("unknown identifier '%s'\n", columns[0]);
    288 			break;
    289 		case P_USAGE:
    290 			printf("invalid usage of command '%s'\n", columns[0]);
    291 			break;
    292 		case P_SCOPE:
    293 			printf("invalid scope of command '%s'\n", columns[0]);
    294 			break;
    295 		case P_DATA:
    296 			printf("invalid paramter-type of '%s'\n", columns[0]);
    297 			break;
    298 		case P_REDEF:
    299 			printf("redefinition of '%s'\n", columns[0]);
    300 			break;
    301 	}
    302 	config_cleanup();
    303 
    304 cleanup:
    305 	if (line_origin != NULL)
    306 		free(line_origin);
    307 
    308 	return result;
    309 }
    310 
    311 void config_free_mount(mount_t* mount, int size) {
    312 	for (int i = 0; i < size; i++) {
    313 		if (mount[i].type != NULL)
    314 			free((void*) mount[i].type);
    315 		if (mount[i].source != NULL)
    316 			free((void*) mount[i].source);
    317 		if (mount[i].target != NULL)
    318 			free((void*) mount[i].target);
    319 		if (mount[i].options != NULL)
    320 			free((void*) mount[i].options);
    321 	}
    322 }
    323 
    324 void config_cleanup() {
    325 	config_free_mount(mounts, mount_size);
    326 	mount_size = 0;
    327 
    328 	for (int i = 0; i < section_size; i++) {
    329 		if (sections[i].name != NULL)
    330 			free((void*) sections[i].name);
    331 		if (sections[i].root != NULL)
    332 			free((void*) sections[i].root);
    333 		if (sections[i].init != NULL)
    334 			free((void*) sections[i].init);
    335 		config_free_mount(sections[i].mounts, sections[i].mount_size);
    336 	}
    337 	section_size = 0;
    338 }
    339 
    340 void config_reset() {
    341 	config_cleanup();
    342 
    343 	section_size = 0;
    344 	mount_size	 = 0;
    345 	color		 = false;
    346 	verbose		 = true;
    347 	timeout		 = 10;
    348 }