/* $Id: summary.c,v 1.41 2009/05/07 21:22:52 imil Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Emile "iMil" Heitor . * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ #include "tools.h" #include "pkg_dry.h" static const struct Summary { const int type; const char *tbl_name; const char *deps; const char *conflicts; const char *requires; const char *provides; } sumsw[] = { { LOCAL_SUMMARY, "LOCAL_PKG", "LOCAL_DEPS", "LOCAL_CONFLICTS", "LOCAL_REQUIRES", "LOCAL_PROVIDES" }, { REMOTE_SUMMARY, "REMOTE_PKG", "REMOTE_DEPS", "REMOTE_CONFLICTS", "REMOTE_REQUIRES", "REMOTE_PROVIDES" }, }; static struct Columns { int num; char **name; } cols; typedef struct Insertlist { char *field; char *value; SLIST_ENTRY(Insertlist) next; } Insertlist; SLIST_HEAD(, Insertlist) inserthead; static char **fetch_summary(char *url); static void freecols(void); static void free_insertlist(void); static void prepare_insert(int, struct Summary); int colnames(void *, int, char **, char **); char **commit_list = NULL; char *cur_repo = NULL; int commit_idx = 0; int query_size = BUFSIZ; /* remote summary fetch */ static char ** fetch_summary(char *url) { /* from pkg_install/files/admin/audit.c */ Dlfile *file; char *decompressed_input; size_t decompressed_len; char **out; file = download_file(url); if (decompress_buffer(file->buf, file->size, &decompressed_input, &decompressed_len)) { out = splitstr(decompressed_input, "\n"); XFREE(file->buf); XFREE(file); return out; } XFREE(file->buf); XFREE(file); return NULL; } /* progress percentage */ static void progress(char c) { const char *alnum = ALNUM; int i, alnumlen = strlen(alnum); float percent = 0; for (i = 0; i < alnumlen; i++) if (c == alnum[i]) percent = ((float)(i + 1)/ (float)alnumlen) * 100; printf("updating database: %.f%%\r", percent); } /* check if the field is PKGNAME */ static int chk_pkgname(char *field) { if (strncmp(field, "PKGNAME=", 8) == 0 || strncmp(field, "CONFLICTS=", 10) == 0) return 1; return 0; } /* returns value for given field */ static char * field_record(const char *field, char *line) { char *pfield; if (strncmp(field, line, strlen(field)) == 0) { pfield = strchr(line, '='); trimcr(pfield++); return pfield; } return NULL; } static void freecols() { int i; for (i = 0; i < cols.num; i++) XFREE(cols.name[i]); XFREE(cols.name); } static void free_insertlist() { Insertlist *pi; while (!SLIST_EMPTY(&inserthead)) { pi = SLIST_FIRST(&inserthead); SLIST_REMOVE_HEAD(&inserthead, next); XFREE(pi->field); XFREE(pi->value); XFREE(pi); } } /* sqlite callback, fill cols.name[] with available columns names */ int colnames(void *unused, int argc, char **argv, char **colname) { int i = 0; static int colcount = 0; colcount++; cols.num = colcount; XREALLOC(cols.name, colcount * sizeof(char *)); for (i = 0; i < argc; i++) if (argv[i] != NULL && strncmp(colname[i], "name", 4) == 0) XSTRDUP(cols.name[colcount - 1], argv[i]); return DDB_OK; } /* for now, values are located on a SLIST, build INSERT line with them */ static void prepare_insert(int pkgid, struct Summary sum) { char *commit_query; Insertlist *pi; if (sum.type == REMOTE_SUMMARY) query_size = (query_size + strlen(cur_repo)) * sizeof(char); XMALLOC(commit_query, query_size); snprintf(commit_query, BUFSIZ, "INSERT INTO %s ( PKG_ID,", sum.tbl_name); /* insert fields */ SLIST_FOREACH(pi, &inserthead, next) snprintf(commit_query, query_size, "%s\"%s\",", commit_query, pi->field); /* insert REPOSITORY field */ if (sum.type == REMOTE_SUMMARY) snprintf(commit_query, query_size, "%s\"REPOSITORY\")", commit_query); else commit_query[strlen(commit_query) - 1] = ')'; snprintf(commit_query, query_size, "%s VALUES ( %d, ", commit_query, pkgid); /* insert values */ SLIST_FOREACH(pi, &inserthead, next) snprintf(commit_query, query_size, "%s\"%s\",", commit_query, pi->value); /* insert repository URL if it's a remote pkg_summary */ if (sum.type == REMOTE_SUMMARY) { snprintf(commit_query, query_size, "%s\"%s\");", commit_query, cur_repo); } else { commit_query[strlen(commit_query) - 1] = ')'; strcat(commit_query, ";"); } /* append query to commit list */ commit_idx++; XREALLOC(commit_list, (commit_idx + 1) * sizeof(char *)); commit_list[commit_idx] = commit_query; } static void child_table(int pkgid, const char *table, char *val) { char buf[BUFSIZ]; snprintf(buf, BUFSIZ, "INSERT INTO %s (PKG_ID,%s_PKGNAME) VALUES (%d,\"%s\");", table, table, pkgid, val); /* append query to commit_list */ commit_idx++; XREALLOC(commit_list, (commit_idx + 1) * sizeof(char *)); XSTRDUP(commit_list[commit_idx], buf); } static void update_col(struct Summary sum, int pkgid, char *line) { int i; char *val, *p, buf[BUFSIZ]; Insertlist *insert; /* DEPENDS */ if ((val = field_record("DEPENDS", line)) != NULL) child_table(pkgid, sum.deps, val); /* REQUIRES */ if ((val = field_record("REQUIRES", line)) != NULL) child_table(pkgid, sum.requires, val); /* PROVIDES */ if ((val = field_record("PROVIDES", line)) != NULL) child_table(pkgid, sum.provides, val); for (i = 0; i < cols.num; i++) { snprintf(buf, BUFSIZ, "%s=", cols.name[i]); val = field_record(cols.name[i], line); /* XXX: handle that later */ if (strncmp(cols.name[i], "DESCRIPTION", 11) == 0) continue; if (val != NULL && strncmp(buf, line, strlen(buf)) == 0) { /* nasty little hack to prevent double quotes */ if (strchr(line, '"') != NULL) for (p = line; *p != '\0'; p++) if (*p == '"') *p = '`'; XMALLOC(insert, sizeof(Insertlist)); XSTRDUP(insert->field, cols.name[i]); XSTRDUP(insert->value, val); SLIST_INSERT_HEAD(&inserthead, insert, next); /* update query size */ query_size += strlen(insert->field) + strlen(insert->value) + 5; /* 5 = strlen(\"\",) */ } } } static void insert_summary(struct Summary sum, char **summary) { int i; static int pkgid = 1; char *pkgname, **psum, query[BUFSIZ]; const char *alnum = ALNUM; Insertlist *insert; printf( "\e[?25l" ); /* hide the cursor */ if (summary == NULL) { drydb_close(); errx(EXIT_FAILURE, "could not open read summary"); } snprintf(query, BUFSIZ, "PRAGMA table_info(%s);", sum.tbl_name); /* record columns names to cols */ drydb_doquery(query, colnames, NULL); SLIST_INIT(&inserthead); XMALLOC(commit_list, sizeof(char *)); /* begin transaction */ XSTRDUP(commit_list[0], "BEGIN;"); psum = summary; /* main pkg_summary analysis loop */ while (*psum != NULL) { /* CONFLICTS may appear before PKGNAME... */ if ((pkgname = field_record("CONFLICTS", *psum)) != NULL) { snprintf(query, BUFSIZ, "INSERT INTO %s (PKG_ID,%s_PKGNAME) VALUES (%d,\"%s\");", sum.conflicts, sum.conflicts, pkgid, pkgname); /* append query to commit_list */ commit_idx++; XREALLOC(commit_list, (commit_idx + 1) * sizeof(char *)); XSTRDUP(commit_list[commit_idx], query); psum++; continue; /* there may be more */ } /* PKGNAME record, should always be true */ if ((pkgname = field_record("PKGNAME", *psum)) != NULL) { XMALLOC(insert, sizeof(Insertlist)); XSTRDUP(insert->field, "PKGNAME"); XSTRDUP(insert->value, pkgname); SLIST_INSERT_HEAD(&inserthead, insert, next); /* nice little counter */ progress(pkgname[0]); } psum++; /* browse entries following PKGNAME and build the SQL query */ while (*psum != NULL && !chk_pkgname(*psum)) { update_col(sum, pkgid, *psum); psum++; } /* build INSERT query */ prepare_insert(pkgid, sum); /* next PKG_ID */ pkgid++; /* free the SLIST containing this package's key/vals */ free_insertlist(); /* reset max query size */ query_size = BUFSIZ; } /* while *psum != NULL */ commit_idx++; XREALLOC(commit_list, (commit_idx + 2) * sizeof(char *)); XSTRDUP(commit_list[commit_idx], "COMMIT;"); commit_list[commit_idx + 1] = NULL; /* do the insert */ for (i = 0; commit_list[i] != NULL; i++) drydb_doquery(commit_list[i], NULL, NULL); progress(alnum[strlen(alnum) - 1]); /* XXX: nasty. */ free_list(commit_list); commit_idx = 0; /* reset pkgid */ if (sum.type == LOCAL_SUMMARY) pkgid = 1; printf( "\n\e[?25h" ); /* show the cursor again */ } void update_db(int which, char **pkgkeep) { int i; Plisthead *plisthead; Pkglist *pkglist; char **summary = NULL, **prepos, buf[BUFSIZ]; plisthead = rec_pkglist(KEEP_LOCAL_PKGS); /* dropping and recreating the tables is faster than updating */ drydb_doquery(DROP_LOCAL_TABLES, NULL, NULL); if (which == REMOTE_SUMMARY) drydb_doquery(DROP_REMOTE_TABLES, NULL, NULL); drydb_doquery(CREATE_DRYDB, NULL, NULL); for (i = 0; i < 2; i++) { switch (sumsw[i].type) { case LOCAL_SUMMARY: /* generate summary locally */ summary = exec_list(PKGTOOLS "/pkg_info -Xa", NULL); printf("processing local summary...\n"); insert_summary(sumsw[i], summary); free_list(summary); break; case REMOTE_SUMMARY: if (which == LOCAL_SUMMARY) continue; /* loop through PKG_REPOS */ for (prepos = pkg_repos; *prepos != NULL; prepos++) { snprintf(buf, BUFSIZ, "%s/%s.bz2", *prepos, PKG_SUMMARY); /* load remote pkg_summary */ summary = fetch_summary(buf); printf("processing remote summary (%s)...\n", *prepos); cur_repo = *prepos; insert_summary(sumsw[i], summary); free_list(summary); } /* remove empty rows (duplicates) */ drydb_doquery(DELETE_EMPTY_ROWS, NULL, NULL); break; } /* restore keep list */ if (sumsw[i].type == LOCAL_SUMMARY) { if (plisthead != NULL) { SLIST_FOREACH(pkglist, plisthead, next) { snprintf(buf, BUFSIZ, KEEP_PKG, pkglist->pkgname); drydb_doquery(buf, NULL, NULL); } free_pkglist(plisthead); XFREE(plisthead); } /* insert new keep list */ if (pkgkeep != NULL) /* installation: mark the packages as "keep" */ pkg_keep(KEEP, pkgkeep); } } /* columns name not needed anymore */ if (cols.name != NULL) freecols(); }