Description: Dependencies loops detector on stage of "rc-update" Origin: https://github.com/xaionaro/openrc Author: Dmitry Yu Okunev 0x8E30679C Bug: https://bugs.gentoo.org/show_bug.cgi?id=391945 Last-Update: 2014-01-26 --- openrc-0.12.4+20131230.orig/src/librc/librc-depend.c +++ openrc-0.12.4+20131230/src/librc/librc-depend.c @@ -29,12 +29,41 @@ */ #include +#include /* hsearch() */ +#include /* uint32_t/uint64_t */ #include "librc.h" +#include "../include/einfo.h" -#define GENDEP RC_LIBEXECDIR "/sh/gendepends.sh" +#define GENDEP RC_LIBEXECDIR "/sh/gendepends.sh" -#define RC_DEPCONFIG RC_SVCDIR "/depconfig" +#define RC_DEPCONFIG RC_SVCDIR "/depconfig" + +#define LOOPSOLVER_LIMIT 128 + +/*! Type definition of service ID */ +typedef uint32_t service_id_t; + +/*! Enumeration of rc_deptree_solve_loop()'s return cases */ +typedef enum loopfound { + LOOP_SOLVABLE = 0x01, + LOOP_UNSOLVABLE = 0x02, +} loopfound_t; + +/* "use, need, after" dependencies matrix types */ +typedef enum unam_type { + UNAM_USE = 0, + UNAM_AFTER = 1, + UNAM_NEED = 2, + UNAM_MIXED = 3, + UNAM_MIXED_EXPANDED = 4, + UNAM_MAX +} unam_type_t; + +typedef struct idid_entry { + uint64_t idid; + void *data; +} idid_entry_t; static const char *bootlevel = NULL; @@ -725,14 +754,688 @@ rc_deptree_update_needed(time_t *newest, } librc_hidden_def(rc_deptree_update_needed) -/* This is a 6 phase operation +static inline int +rc_deptree_unam_expandsdeps(service_id_t **una, service_id_t service_id) +{ + int dep_num, dep_count; + int ismodified; + + ismodified = 0; + dep_num = 0; + dep_count = una[service_id][0]; + while (dep_num < dep_count) { + service_id_t dep_service_id; + int dep_dep_num, dep_dep_count; + + dep_num++; + dep_service_id = una[service_id][dep_num]; + /*printf("service_id == %i; dep_num == %i (%i); dep_service_id == %i\n", service_id, dep_num, dep_count, dep_service_id);*/ + + dep_dep_num = 0; + dep_dep_count = una[dep_service_id][0]; + + while (dep_dep_num < dep_dep_count) { + int istoadd, dep_num_2; + service_id_t dep_dep_service_id; + dep_dep_num++; + + dep_dep_service_id = una[dep_service_id][dep_dep_num]; + + istoadd = 1; + dep_num_2 = 0; + while (dep_num_2 < dep_count) { + dep_num_2++; + if (dep_dep_service_id == una[service_id][dep_num_2]) { + istoadd = 0; + break; + } + } + + if (istoadd) { + ismodified = 1; + dep_count++; + una[service_id][dep_count] = dep_dep_service_id; + una[service_id][0] = dep_count; + } + } + } + + return ismodified; +} + +/*! Fills dependency matrix for further loop detection + * @param una_matrix matrix to fill + * @param useneedafter_count number of use/need/after dependencies + * @param service_id ID of the service for dependency scanning + * @param type dependencies type + * @param depinfo dependencies information */ +static void +rc_deptree_unam_getdependencies(service_id_t **una_matrix, + int useneedafter_count, service_id_t service_id, + const char *type, RC_DEPINFO *depinfo) +{ + RC_STRING *svc, *svc_np; + RC_DEPTYPE *deptype; + + una_matrix[service_id] = xcalloc((useneedafter_count+1), sizeof(**una_matrix)); + + deptype = get_deptype(depinfo, type); + if (deptype == NULL) + return; + + TAILQ_FOREACH_SAFE(svc, deptype->services, entries, svc_np) { + ENTRY item, *item_p; + service_id_t dependon; + + item.key = svc->value; + + item_p = hsearch(item, FIND); + if (item_p == NULL) /* Deadend branch, no sense to continue checking it anyway */ + continue; + + dependon = (int)(long int)item_p->data; + + if (dependon == service_id) + continue; /* To prevent looping detection services on themselves (for example in case of depending on '*') */ + + una_matrix[service_id][ ++una_matrix[service_id][0] ] = dependon; + } + + return; +} + +static int +svc_id2depinfo_bt_compare(const void *a, const void *b) +{ + return ((const ENTRY *)a)->key - ((const ENTRY *)b)->key; +} + +static int +idid_compare(const void *a, const void *b) +{ + return ((const idid_entry_t *)a)->idid - ((const idid_entry_t *)b)->idid; +} + +static int +idid_compare_data_desc(const void *a, const void *b) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-arith" + return ((const idid_entry_t *)b)->data - ((const idid_entry_t *)a)->data; +#pragma GCC diagnostic pop +} + +/*! Solves dependecies loops + * @param una_matrix matrixes to scan ways to solve the loop + * @param service_id looped service id + * @param svc_id2depinfo_bt ptr to binary tree root to get depinfo by svc id + * @param end_dep_num looping dependency id in use/need/after matrix line */ +static loopfound_t +rc_deptree_solve_loop(service_id_t **una_matrix[UNAM_MAX], service_id_t service_id, void *svc_id2depinfo_bt, int end_dep_num) { + char **chain_strs; + service_id_t **chains; + unam_type_t **deptypes; + unam_type_t minimal_cost; + int chains_size = una_matrix[0][0][0], chain_count; + + /* The later dependency can be caused only by earlier dependency + and dep_num-s of earlier dependencies is less the later ones */ + + /* + S - service + D - dependency on not S + X - dependency on S (looper) + + One UNA matrix line meaning: + + S D D D D D D D D D D D D X D D + + + Using information about individual services dependecies, restoring: + _____ + |___ | ___ ___ ___ __ + |_ | | |_ | | _| |_ | | + S D D D D D D_D D D D_D D X D D ... + | |___| | |___| | | |___| | + |_______| |_____| | |_____| + |_________| + + Without extra: + + U - use + N - need + A - after + + _U_ _A_ _U_ _N_ + | | | | | _| | | + S D D D D D D_D X + |_N_| |_U_| | | + |__A__| | + |____N____| + + + Removing weak dependencies (use and after) + to avoid the loop + + ___ ___ ___ + | | | _| | | + S D D D D D D_D X + |___| |___| | | + |_____| | + |_________| + + + The result: + _____ + | | + |_ | + S D D D + | | + |_______| + + + After that the UNA matrix (that is the mixed one) will be stale. + It will be need to recalculate dependencies in it + and recheck for loops. + */ + + chains = xmalloc(chains_size * sizeof(*chains)); + + /* building all dependency chains of the service */ + + { + /* + __ + / \ + /\/\ \ + 1 2 3 4 5 1 + \/ / \/\/ + \/ + + 1 2 3 4 5 6 + + 1 2 5 1 + 1 3 4 5 1 + 1 4 5 1 + 1 2 3 4 5 1 + */ + + int dep_num, una_line_size; + service_id_t *una_line; + + una_line_size = una_matrix[UNAM_MIXED_EXPANDED][service_id][0]+1; + una_line = xmalloc(una_line_size * sizeof(*una_line)); + + memcpy(una_line, una_matrix[UNAM_MIXED_EXPANDED][service_id], una_line_size * sizeof(*una_line)); + + una_line[0] = service_id; + chain_count = 0; + dep_num = -1; + while (++dep_num < end_dep_num) { + int dep_dep_num, added_count; + service_id_t dep_service_id; + + dep_service_id = una_line[dep_num]; + dep_dep_num = dep_num; + added_count = 0; + while (++dep_dep_num <= end_dep_num) { + int chain_num, chain_count_new, dep_dep_check_num, dep_dep_check_count, istobeadded; + service_id_t dep_dep_service_id, dep_dep_service_id_added; + + dep_dep_service_id = una_line[dep_dep_num]; + + dep_dep_check_num = 0; + dep_dep_check_count = una_matrix[UNAM_MIXED][dep_service_id][0]; + istobeadded = 0; + while (dep_dep_check_num++ < dep_dep_check_count) { + if (una_matrix[UNAM_MIXED][dep_service_id][dep_dep_check_num] == dep_dep_service_id) { + istobeadded = 1; + break; + } + } + + if (istobeadded) { + int chain_len; + + chain_num = 0; + chain_count_new = chain_count; + +#define CHAINS_CHECK_SIZE {\ + if (chain_count_new >= chains_size) {\ + chains_size += una_matrix[0][0][0];\ + chains = xrealloc(chains, chains_size*sizeof(*chains));\ + }\ + /*printf("A: %i\n", chain_count_new);*/\ + chains[chain_count_new] = xmalloc(end_dep_num*sizeof(**chains));\ + chains[chain_count_new][0] = 0;\ + } + + if (dep_num == 0) { + CHAINS_CHECK_SIZE; + chains[chain_count_new][1] = dep_dep_service_id; + chains[chain_count_new][0] = 1; + chain_count_new++; + } else + while (chain_num < chain_count) { + if (!added_count) { + chain_len = chains[chain_num][0]; + if (chains[chain_num][chain_len] == dep_service_id) { + chains[chain_num][++chain_len] = dep_dep_service_id; + dep_dep_service_id_added = dep_dep_service_id; + chains[chain_num][0] = chain_len; + } + } else { + /* required chains were been enlarged by previous iteration, so "-1" */ + chain_len = chains[chain_num][0]-1; + if (chains[chain_num][chain_len] == dep_service_id && chains[chain_num][chain_len+1] == dep_dep_service_id_added) { + CHAINS_CHECK_SIZE; + memcpy(chains[chain_count_new], chains[chain_num], (chain_len+1)*sizeof(**chains)); + chains[chain_count_new][++chain_len] = dep_dep_service_id; + chains[chain_count_new][0] = chain_len; + chain_count_new++; + } + } + chain_num++; + } + added_count++; + chain_count = chain_count_new; + } + } + } + + free(una_line); + } + + /* removing non-looping chains */ + + { + int i; + + i = 0; + while (i < chain_count) { + int j, chain_len, islooping; + + chain_len = chains[i][0]; + + islooping = 0; + j = 0; + while (j++ < chain_len) + if (chains[i][j] == service_id) { + islooping = 1; + break; + } + + if (!islooping) { + free(chains[i]); + chains[i] = chains[--chain_count]; + continue; + } + + i++; + } + } + + /* getting dependencies types */ + + { + int i; + deptypes = xmalloc(chain_count * sizeof(*deptypes)); + + i = 0; + while (i < chain_count) { + int j, chain_len; + + chain_len = chains[i][0]; + + deptypes[i] = xmalloc((chain_len+1) * sizeof(**deptypes)); + + j = 0; + while (j++ < chain_len) { + service_id_t dep_service_id_from, dep_service_id_to; + unam_type_t type; + + dep_service_id_from = j==1 ? service_id : chains[i][j-1]; + dep_service_id_to = chains[i][j]; + + type = UNAM_NEED+1; + while (type-- > 0) { + int dep_dep_num, dep_dep_count; + dep_dep_num = 0; + dep_dep_count = una_matrix[type][dep_service_id_from][0]; + + while (dep_dep_num++ < dep_dep_count) { + if (una_matrix[type][dep_service_id_from][dep_dep_num] == dep_service_id_to) { + deptypes[i][j] = type; + type = 0; /* to break parent while (), too */ + break; + } + } + } + } + i++; + } + } + + /* printing */ + + { + int chain_num; + + chain_strs = xmalloc(chain_count * sizeof(*chain_strs)); + + chain_num = 0; + while (chain_num < chain_count) { + char *chain_str, *chain_str_end; + int chain_len; + + chain_str = chain_strs[chain_num] = xmalloc(BUFSIZ); + chain_str_end = &chain_str[BUFSIZ-2]; + + /* Preparing a string of services forming the loop */ + { + char *ptr_dst; + int i; + + ptr_dst = chain_str; + + chain_len = chains[chain_num][0]; + chains[chain_num][0] = service_id; + + i = chain_len+1; + while (i--) { + ENTRY item, **item_pp; + RC_DEPINFO *depinfo; + const char *service_name, *ptr_src; + + item.key = (void *)(long)chains[chain_num][i]; + item_pp = tfind(&item, &svc_id2depinfo_bt, svc_id2depinfo_bt_compare); + depinfo = (RC_DEPINFO *)((ENTRY *)*item_pp)->data; + + service_name = depinfo->service; + + ptr_src = service_name; + while (*ptr_src && (ptr_dst < chain_str_end)) + *(ptr_dst++) = *(ptr_src++); + + if (ptr_dst >= chain_str_end) { + ptr_dst--; + break; + } + + if (i) { + if (&ptr_dst[4] >= chain_str_end) + break; + + memcpy(ptr_dst, " -> ", 4); + ptr_dst += 4; + } + } + + chains[chain_num][0] = chain_len; + + *ptr_dst = 0; + } + +/* + { + int j; + chain_len = chains[chain_num][0]; + j = 0; + printf("%i: %i (%i):", service_id, chain_num, chain_len); + while (j++ < chain_len) + printf(" %i<%i>", chains[chain_num][j], deptypes[chain_num][j]); + printf("\n"); + } +*/ + chain_num++; + } + + } + + /* checking the cost of loop solving (use/after/need) */ + + { + int i; + + minimal_cost = 0; + i = 0; + while (i < chain_count) { + int j, chain_len; + ENTRY item, **item_pp; + RC_DEPINFO *depinfo; + unam_type_t chain_cost; + + chain_cost = UNAM_MAX; + + chain_len = chains[i][0]; + j = 0; + while (j++ < chain_len) + chain_cost = MIN(chain_cost, deptypes[i][j]); + + minimal_cost = MAX(minimal_cost, chain_cost); + + item.key = (void *)(long)service_id; + item_pp = tfind(&item, &svc_id2depinfo_bt, svc_id2depinfo_bt_compare); + depinfo = (RC_DEPINFO *)((ENTRY *)*item_pp)->data; + if (minimal_cost > UNAM_AFTER) + eerror("Found an unresolvable dependencies loop with service %s: %s.", depinfo->service, chain_strs[i]); + else + ewarn("Found a solvable dependencies loop with service %s: %s.", depinfo->service, chain_strs[i]); + i++; + } + /*printf("minimal cost: %i\n", minimal_cost);*/ + + if (minimal_cost > UNAM_AFTER) + return LOOP_UNSOLVABLE; + } + + /* calculating optimal way to solve the loop and solving it */ + + { + void *btree = NULL; + uint64_t *idid_to_break; + int idid_count, idid_to_break_count; + idid_to_break = xmalloc(chain_count * sizeof(*idid_to_break)); + + /* counting a presence of each dependency through all chains */ + { + int i; + + idid_count = 0; + i = 0; + while (i < chain_count) { + int j, chain_len; + + chain_len = chains[i][0]; + j = 0; + while (j++ < chain_len) { + uint64_t idid; + service_id_t service_id_from, service_id_to; + idid_entry_t idid_entry, *idid_entry_p; + void *tfind_res; + + if (deptypes[i][j] > minimal_cost) /* we don't break this dependency, skipping */ + continue; + + service_id_from = j>1 ? chains[i][j-1] : service_id; + service_id_to = chains[i][j]; + + idid = ((uint64_t)service_id_from << bitsizeof(service_id_to)) | service_id_to; + + idid_entry.idid = idid; + tfind_res = tfind(&idid_entry, &btree, idid_compare); + /*printf("A: (%i -> %i) %li: %p: %p\n", service_id_from, service_id_to, idid, tfind_res, tfind_res==NULL?NULL:*(idid_entry_t **)tfind_res);*/ + if (tfind_res == NULL) { + idid_entry_p = xmalloc(sizeof(*idid_entry_p)); + idid_entry_p->idid = idid; + idid_entry_p->data = (void *)1; + tsearch(idid_entry_p, &btree, idid_compare); + idid_count++; + } else { + idid_entry_p = *(idid_entry_t **)tfind_res; + idid_entry_p->data = (void *)((long)idid_entry_p->data + 1); + } + } + + i++; + } + } + + /* building array of dependencies sorted by descending presence counter */ + + { + int idid_count2; + idid_entry_t *idid_counters; + + void idid_btree_builddescarray(const void *nodep, const VISIT which, const int depth) { + (void)depth; + switch (which) { + case preorder: + case leaf: { + const idid_entry_t *idid_entry_p = *(idid_entry_t * const*)nodep; + + memcpy(&idid_counters[idid_count2], idid_entry_p, sizeof(idid_counters[idid_count2])); + idid_count2++; + break; + } + default: + break; + } + return; + } + + idid_counters = xmalloc(idid_count * sizeof(*idid_counters)); + + idid_count2 = 0; + + twalk(btree, idid_btree_builddescarray); + + qsort(idid_counters, idid_count2, sizeof(*idid_counters), idid_compare_data_desc); + + idid_to_break_count = MIN(idid_count2, chain_count); + idid_count2 = 0; + while (idid_count2 < idid_to_break_count) { + idid_to_break[idid_count2] = idid_counters[idid_count2].idid; + /*printf("B: %li %li\n", idid_counters[idid_count2].idid, (long)idid_counters[idid_count2].data);*/ + idid_count2++; + } + + free(idid_counters); + } + + /* solving loops */ + + { + idid_count = 0; + while (idid_count < idid_to_break_count) { + service_id_t service_id_from, service_id_to; + + /* IDID: 12345678901234567890123456789012 34567890123456789012345678901234 + service_id_from service_id_to */ + + service_id_from = ((uint64_t)idid_to_break[idid_count] & ((uint64_t)((service_id_t)~0) << bitsizeof(service_id_t))) >> bitsizeof(service_id_t); + service_id_to = idid_to_break[idid_count] & ((service_id_t)~0); + + idid_count++; + + { + ENTRY item, **item_pp; + RC_DEPINFO *depinfo_from, *depinfo_to; + + void + rc_deptree_remove_loopdependency(service_id_t **una[UNAM_MAX], service_id_t dep_remove_from_service_id, service_id_t dep_remove_to_service_id, RC_DEPINFO *di_from, RC_DEPINFO *di_to, const char *const type, unam_type_t unam_type) + { + RC_DEPTYPE *deptype_from, *deptype_to; + int dep_num, dep_count; + const char *type_reverse = NULL; + int deptype_num; + + deptype_from = get_deptype(di_from, type); + if (deptype_from != NULL) { + rc_stringlist_delete(deptype_from->services, di_to->service); + + dep_num = 0; + dep_count = una[unam_type][dep_remove_from_service_id][0]; + /*printf("CUT SEARCH INIT: %i %i\n", unam_type, dep_count);*/ + while (dep_num++ < dep_count) { + /*printf("CUT SEARCH: %i: %i %i %i\n", dep_remove_from_service_id, unam_type, dep_num, una[unam_type][dep_remove_from_service_id][dep_num]);*/ + if (una[unam_type][dep_remove_from_service_id][dep_num] == dep_remove_to_service_id) + una[unam_type][dep_remove_from_service_id][dep_num] = + una[unam_type][dep_remove_from_service_id][dep_count--]; + } + una[unam_type][dep_remove_from_service_id][0] = dep_count; + } + + deptype_num = 0; + while (deppairs[deptype_num].depend) { + if (!strcmp(deppairs[deptype_num].depend, type)) { + type_reverse = deppairs[deptype_num].addto; + break; + } + deptype_num++; + } + + deptype_to = get_deptype(di_to, type_reverse); + if (deptype_to != NULL) + rc_stringlist_delete(deptype_to->services, di_from->service); + + return; + } + + item.key = (void *)(long)service_id_from; + item_pp = tfind(&item, &svc_id2depinfo_bt, svc_id2depinfo_bt_compare); + depinfo_from = (RC_DEPINFO *)((ENTRY *)*item_pp)->data; + + item.key = (void *)(long)service_id_to; + item_pp = tfind(&item, &svc_id2depinfo_bt, svc_id2depinfo_bt_compare); + depinfo_to = (RC_DEPINFO *)((ENTRY *)*item_pp)->data; + + /* Remove weak dependency */ + + ewarn("Solving loops by removing use/after dependencies of %s on %s from the cache.", depinfo_from->service, depinfo_to->service); + /*printf("DEP CUT: %i %i\n", dep_remove_from_service_id, dep_remove_to_service_id);*/ + + rc_deptree_remove_loopdependency(una_matrix, service_id_from, service_id_to, depinfo_from, depinfo_to, "iuse", UNAM_USE); + rc_deptree_remove_loopdependency(una_matrix, service_id_from, service_id_to, depinfo_from, depinfo_to, "iafter", UNAM_AFTER); + + } + } + } + + /* cleanup */ + + tdestroy(btree, free); + free(idid_to_break); + } + + /* cleanup */ + { + int i; + + i = 0; + while (i < chain_count) { + free(chain_strs[i]); + free(deptypes[i]); + free(chains[i]); + i++; + } + free(chain_strs); + free(deptypes); + free(chains); + } + + return LOOP_SOLVABLE; +} + +/* This is a 7 phase operation Phase 1 is a shell script which loads each init script and config in turn and echos their dependency info to stdout Phase 2 takes that and populates a depinfo object with that data Phase 3 adds any provided services to the depinfo object Phase 4 scans that depinfo object and puts in backlinks Phase 5 removes broken before dependencies - Phase 6 saves the depinfo object to disk + Phase 6 check for loops + Phase 7 saves the depinfo object to disk */ bool rc_deptree_update(void) @@ -750,6 +1453,7 @@ rc_deptree_update(void) bool retval = true; const char *sys = rc_sys(); struct utsname uts; + unsigned int useneedafter_count=0; /* Some init scripts need RC_LIBEXECDIR to source stuff Ideally we should be setting our full env instead */ @@ -951,6 +1660,8 @@ rc_deptree_update(void) rc_stringlist_add(types, "iuse"); rc_stringlist_add(types, "iafter"); TAILQ_FOREACH(depinfo, deptree, entries) { + useneedafter_count++; + deptype = get_deptype(depinfo, "ibefore"); if (!deptype) continue; @@ -994,7 +1705,170 @@ rc_deptree_update(void) } rc_stringlist_free(types); - /* Phase 6 - save to disk + /* Phase 6 - check for loops (non-recursive way) */ + { + int loopfound; + unam_type_t unam_type; + service_id_t **una_matrix[UNAM_MAX]; + service_id_t service_id; + void *svc_id2depinfo_bt = NULL; + int loopsolver_counter = 0; + + hcreate(useneedafter_count*2); + + unam_type = 0; + while (unam_type < UNAM_MAX) + una_matrix[unam_type++] = xmalloc(sizeof(*una_matrix) * (useneedafter_count+1)); + + /* preparing hash-table: service_name -> service_id */ + service_id = 1; + TAILQ_FOREACH(depinfo, deptree, entries) { + ENTRY item, *item_p; + + item.key = depinfo->service; + item.data = (void *)(long int)service_id; + hsearch(item, ENTER); + + item_p = xmalloc(sizeof(*item_p)); + item_p->key = (void *)(long int)service_id; + item_p->data = depinfo; + /*printf("%i %p %p %s\n", service_id, item_p, depinfo, depinfo->service);*/ + tsearch(item_p, &svc_id2depinfo_bt, svc_id2depinfo_bt_compare); + + service_id++; + } + + una_matrix[0][0] = xmalloc(sizeof(***una_matrix)*1); + una_matrix[0][0][0] = service_id; + + /* getting dependencies pre-matrixes */ + service_id = 1; + TAILQ_FOREACH(depinfo, deptree, entries) { + rc_deptree_unam_getdependencies(una_matrix[UNAM_USE], useneedafter_count, service_id, "iuse", depinfo); + rc_deptree_unam_getdependencies(una_matrix[UNAM_NEED], useneedafter_count, service_id, "ineed", depinfo); + rc_deptree_unam_getdependencies(una_matrix[UNAM_AFTER], useneedafter_count, service_id, "iafter", depinfo); + service_id++; + } + + hdestroy(); + + /* getting pre-matrix of all dependencies types together (allocating memory) */ + + service_id = 1; + while (service_id < (useneedafter_count+1)) { + una_matrix[UNAM_MIXED ][service_id] = xmalloc((useneedafter_count+1) * sizeof(**una_matrix)); + una_matrix[UNAM_MIXED_EXPANDED][service_id] = xmalloc((useneedafter_count+1) * sizeof(**una_matrix)); + service_id++; + } + + do { + int onemorecycle; + + loopfound = 0; + + /* getting pre-matrix of all dependencies types together */ + + service_id = 1; + while (service_id < (useneedafter_count+1)) { + service_id_t services_dst; + memset(una_matrix[UNAM_MIXED][service_id], 0, (useneedafter_count+1) * sizeof(**una_matrix)); + services_dst = 0; + unam_type = 0; + while (unam_type < UNAM_MIXED) { + service_id_t services_src; + + services_src = una_matrix[unam_type][service_id][0]; + while (services_src) { + /*printf("%i %i: %i %i %i\n", service_id, services_dst+1, unam_type, services_src, + una_matrix[unam_type][service_id][services_src]);*/ + una_matrix[UNAM_MIXED][service_id][ ++services_dst ] = + una_matrix[unam_type][service_id][ services_src-- ]; + } + unam_type++; + } + una_matrix[UNAM_MIXED][service_id][0] = services_dst; + service_id++; + } + + /* preparing full dependencies matrix */ + + /* copying UNAM_MIXED -> UNAM_MIXED_EXPANDED */ + service_id = 1; + while (service_id < (useneedafter_count+1)) { + memcpy(una_matrix[UNAM_MIXED_EXPANDED][service_id], una_matrix[UNAM_MIXED][service_id], (useneedafter_count+1) * sizeof(**una_matrix)); + service_id++; + } + + /* this's the most important and difficult stage: + preparing full dependencies matrix (non-recursive way) + + IMHO, complexity of the algorithm is about: + o((services count)^1*(average dependencies count per service)^3) + O((services count)^2*(average dependencies count per service)^3) */ + + /* In this loop we are adding to own dependencies of depending on services + We are doing it by two directions per cycle: direct and revert. + Empirically it allows to greatly reduce number of iterations */ + + /* getting new dependencies while there're countinuing to arrive. + However there's a hypothesis, that only one iteration is already + enough to detect loops, so the next "do" and "while ()" can be + removed, but then will be no guarantee of loop detection until + the hypothesis will be proved */ + do { + onemorecycle = 0; + + /* direct way: service_id = 1 -> end */ + service_id = 1; + while (service_id < (useneedafter_count+1)) + onemorecycle += rc_deptree_unam_expandsdeps(una_matrix[UNAM_MIXED_EXPANDED], service_id++); + + /* reverse way: service_id = end -> 1 */ + while (--service_id) + onemorecycle += rc_deptree_unam_expandsdeps(una_matrix[UNAM_MIXED_EXPANDED], service_id); + + } while (onemorecycle); + + /* detecting and solving loop (non-recursive method) */ + /* the loop is a situation where service is depended on itself */ + service_id=1; + while ((service_id < (useneedafter_count+1)) && !loopfound) { + int dep_num, dep_count; + + dep_num = 0; + dep_count = una_matrix[UNAM_MIXED_EXPANDED][service_id][0]; + while (dep_num < dep_count) { + dep_num++; + if (una_matrix[UNAM_MIXED_EXPANDED][service_id][dep_num] == service_id) { + loopfound = rc_deptree_solve_loop(una_matrix, service_id, svc_id2depinfo_bt, dep_num); + loopsolver_counter++; + break; + } + } + + service_id++; + } + } while (loopfound == LOOP_SOLVABLE && loopsolver_counter < LOOPSOLVER_LIMIT); + + if (loopsolver_counter >= LOOPSOLVER_LIMIT) + eerror("Dependencies loop solver reached iterations limit."); + + /* clean up */ + + /* una_matrix */ + + unam_type = 0; + while (unam_type < UNAM_MAX) { + service_id=1; + while (service_id < (useneedafter_count+1)) + free(una_matrix[unam_type][service_id++]); + free(una_matrix[unam_type++]); + } + + tdestroy(svc_id2depinfo_bt, free); + } + + /* Phase 7 - save to disk Now that we're purely in C, do we need to keep a shell parseable file? I think yes as then it stays human readable This works and should be entirely shell parseable provided that depend --- a/src/includes/helpers.h +++ b/src/includes/helpers.h @@ -31,6 +31,7 @@ #define __HELPERS_H__ #define ERRX fprintf (stderr, "out of memory\n"); exit (1) +#define bitsizeof(a) (CHAR_BIT*sizeof(a)) #define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) @@ -96,6 +97,17 @@ _unused static void *xmalloc (size_t size) /* NOTREACHED */ } +_unused static void *xcalloc(size_t nmemb, size_t size) +{ + void *value = calloc(nmemb, size); + + if (value) + return (value); + + ERRX; + /* NOTREACHED */ +} + _unused static void *xrealloc(void *ptr, size_t size) { void *value = realloc(ptr, size); @@ -123,6 +135,30 @@ _unused static char *xstrdup(const char *str) /* NOTREACHED */ } +#ifndef _GNU_SOURCE +typedef struct tree_node { + void *data; + struct tree_node *left, *right; +} tree_node_t; + +_unused static void tdestroy(tree_node_t *root, void (*free_data)(void *)) +{ + tree_node_t *node = root; + + if (!node) + return; + + tdestroy(node->left, free_data); + tdestroy(node->right, free_data); + + free_data((void*)(node->data)); + + free(node); + + return; +} +#endif + #undef ERRX /* basename_c never modifies the argument. As such, if there is a trailing --- a/src/librc/Makefile +++ b/src/librc/Makefile @@ -5,9 +5,10 @@ INCS= rc.h VERSION_MAP= rc.map -LDADD+= ${LIBKVM} +LDADD+= ${LIBKVM} -leinfo -CPPFLAGS+= -I../includes +CPPFLAGS+= -I../includes -I../libeinfo +LDFLAGS+= -L../libeinfo MK= ../../mk include ${MK}/lib.mk