Beispiel #1
0
/* verify that required db files exist */
static int check_db_files(alpm_pkg_t *pkg)
{
	const char *pkgname = alpm_pkg_get_name(pkg);
	char *dbpath;
	int ret = 0;

	if((dbpath = get_db_path(pkg, "desc")) == NULL) {
		pu_ui_warn("%s: '%s' read error (%s)", pkgname, dbpath, strerror(errno));
	} else if(check_file(pkgname, dbpath, 0) != 0) {
		ret = 1;
	}

	if((dbpath = get_db_path(pkg, "files")) == NULL) {
		pu_ui_warn("%s: '%s' read error (%s)", pkgname, dbpath, strerror(errno));
	} else if(check_file(pkgname, dbpath, 0) != 0) {
		ret = 1;
	}

	if(!require_mtree) { return ret; }

	if((dbpath = get_db_path(pkg, "mtree")) == NULL) {
		pu_ui_warn("%s: '%s' read error (%s)", pkgname, dbpath, strerror(errno));
	} else if(check_file(pkgname, dbpath, 0) != 0) {
		ret = 1;
	}

	return ret;
}
Beispiel #2
0
int main(int argc, const char **argv) {
	
	usage(argc);
	get_db_path(argc, argv);
	get_name(argc, argv);
	open_db();
		create_table();
		generate_keypair();
		insert_keypair();
    	close_db();
	return 0;
}
Beispiel #3
0
/* --------------------------------
 * init_postgres
 *		Initialize POSTGRES.
 *
 * The database can be specified by name, using the in_dbname parameter, or by
 * OID, using the dboid parameter.	In the latter case, the actual database
 * name can be returned to the caller in out_dbname.  If out_dbname isn't
 * NULL, it must point to a buffer of size NAMEDATALEN.
 *
 * In bootstrap mode no parameters are used.  The autovacuum launcher process
 * doesn't use any parameters either, because it only goes far enough to be
 * able to read pg_database; it doesn't connect to any particular database.
 * In walsender mode only username is used.
 *
 * As of PostgreSQL 8.2, we expect init_process() was already called, so we
 * already have a struct proc struct ... but it's not completely filled in yet.
 *
 * Note:
 *		Be very careful with the order of calls in the init_postgres function.
 * --------------------------------
 */
void init_postgres(const char *in_dbname, oid_t dboid, const char *username, char *out_dbname)
{
	bool bootstrap = BOOTSTRAP_MODE();
	bool am_superuser;
	char *fullpath;
	char dbname[NAMEDATALEN];

	elog(DEBUG3, "init_postgres");

	/*
	 * Add my struct proc struct to the ProcArray.
	 *
	 * Once I have done this, I am visible to other backends!
	 */
	init_proc_phase2();

	/*
	 * Initialize my entry in the shared-invalidation manager's array of
	 * per-backend data.
	 *
	 * Sets up current_bid, a unique backend identifier.
	 */
	current_bid = INVALID_BKNID;
	sci_bkn_init(false);
	if (current_bid > MAX_NR_BACKENDS || current_bid <= 0)
		elog(FATAL, "bad backend ID: %d", current_bid);

	/* Now that we have a bid_t, we can participate in ProcSignal */
	signal_init(current_bid);

	/*
	 * bufmgr needs another initialization call too
	 */
	init_buffer_pool_bkn();

	/*
	 * Initialize local process's access to XLOG.
	 */
	if (child) {
		/*
		 * The postmaster already started the XLOG machinery, but we need to
		 * call init_xlog_access(), if the system isn't in hot-standby mode.
		 * This is handled by calling recovery_in_progres and ignoring the
		 * result.
		 */
		(void) recovery_in_progres();
	} else {
		/*
		 * We are either a bootstrap process or a standalone backend. Either
		 * way, start up the XLOG machinery, and register to have it closed
		 * down at exit.
		 */
		startup_xlog();
		on_shmem_exit(shutdown_xlog, 0);
	}

	/*
	 * Initialize the relation cache and the system catalog caches.  Note that
	 * no catalog access happens here; we only set up the hashtable structure.
	 * We must do this before starting a transaction because transaction abort
	 * would try to touch these hashtables.
	 */
	relcache_init_phase1();
	init_catcache_phase1();
	init_plan_cache();

	/* Initialize portal manager */
	start_portal();

	/* Initialize stats collection --- must happen before first xact */
	if (!bootstrap)
		stat_init();

	/*
	 * Load relcache entries for the shared system catalogs. This must 
	 * create at least entries for pg_database and catalogs used for
	 * authentication.
	 */
	relcache_init_phase2();

	/*
	 * Set up process-exit callback to do pre-shutdown cleanup.  This has to
	 * be after we've initialized all the low-level modules like the buffer
	 * manager, because during shutdown this has to run before the low-level
	 * modules start to close down.  On the other hand, we want it in place
	 * before we begin our first transaction --- if we fail during the
	 * initialization transaction, as is entirely possible, we need the
	 * AbortTransaction call to clean up.
	 */
	on_shmem_exit(ShutdownPostgres, 0);

	/* The autovacuum launcher is done here */
	if (is_avl_proc())
		return;

	/*
	 * Start a new transaction here before first access to db, and get a
	 * snapshot. We don't have a use for the snapshot itself, but we're
	 * interested in the secondary effect that it sets recent_global_xmin. (This
	 * is critical for anything that reads heap pages, because HOT may decide
	 * to prune them even if the process doesn't attempt to modify any
	 * tuples.)
	 */
	if (!bootstrap) {
		/* statement_timestamp must be set for timeouts to work correctly */
		set_current_stmt_start();
		start_xact_cmd();
		(void) get_xact_snap();
	}

	/*
	 * Perform client authentication if necessary, then figure out our
	 * postgres user ID, and see if we are a superuser.
	 *
	 * In standalone mode and in autovacuum worker processes, we use a fixed
	 * ID, otherwise we figure it out from the authenticated user name.
	 */
	if (bootstrap || is_avw_proc()) {
		init_session_uidStandalone();
		am_superuser = true;
	} else if (!child) {
		init_session_uidStandalone();
		am_superuser = true;
		if (!ThereIsAtLeastOneRole())
			ereport(WARNING, (
			errcode(E_UNDEFINED_OBJECT),
			errmsg("no roles are defined in this database system"),
			errhint("You should immediately run CREATE USER \"%s\" SUPERUSER;.",
				username)));
	} else {
		/* normal multiuser case */
		ASSERT(proc_port != NULL);
		PerformAuthentication(proc_port);
		init_session_uid(username);
		am_superuser = superuser();
	}

	/*
	 * If we're trying to shut down, only superusers can connect, and new
	 * replication connections are not allowed.
	 */
	if ((!am_superuser || am_walsender) &&
		proc_port != NULL &&
		proc_port->canAcceptConnections == CAC_WAITBACKUP)
	{
		if (am_walsender)
			ereport(FATAL, (
			errcode(E_INSUFFICIENT_PRIVILEGE),
			errmsg("new replication connections are not allowed during database shutdown")));
		else
			ereport(FATAL, (
			errcode(E_INSUFFICIENT_PRIVILEGE),
			errmsg("must be superuser to connect during database shutdown")));
	}

	/*
	 * Binary upgrades only allowed super-user connections
	 */
	if (is_binary_upgrade && !am_superuser) {
		ereport(FATAL, (
		errcode(E_INSUFFICIENT_PRIVILEGE),
		errmsg("must be superuser to connect in binary upgrade mode")));
	}

	/*
	 * The last few connections slots are reserved for superusers. Although
	 * replication connections currently require superuser privileges, we
	 * don't allow them to consume the reserved slots, which are intended for
	 * interactive use.
	 */
	if ((!am_superuser || am_walsender) &&
		reserved_bknds > 0 &&
		!have_nfree_procs(reserved_bknds))
		ereport(FATAL, (
		errcode(E_TOO_MANY_CONNECTIONS),
		errmsg("remaining connection slots are reserved for non-replication superuser connections")));

	/*
	 * If walsender, we don't want to connect to any particular database. Just
	 * finish the backend startup by processing any options from the startup
	 * packet, and we're done.
	 */
	if (am_walsender) {
		ASSERT(!bootstrap);

		/* must have authenticated as a replication role */
		if (!is_authenticated_user_replication_role())
			ereport(FATAL, (
			errcode(E_INSUFFICIENT_PRIVILEGE),
			errmsg("must be replication role to start walsender")));

		/* process any options passed in the startup packet */
		if (proc_port != NULL)
			process_startup_options(proc_port, am_superuser);

		/* Apply post_auth_delay as soon as we've read all options */
		if (post_auth_delay > 0)
			pg_usleep(post_auth_delay * 1000000L);

		/* initialize client encoding */
		init_client_encoding();

		/* report this backend in the struct backend_status array */
		stat_backend_start();

		/* close the transaction we started above */
		commit_xact_cmd();

		return;
	}

	/*
	 * Set up the global variables holding database id and default tablespace.
	 * But note we won't actually try to touch the database just yet.
	 *
	 * We take a shortcut in the bootstrap case, otherwise we have to look up
	 * the db's entry in pg_database.
	 */
	if (bootstrap) {
		current_db_id = TemplateDbOid;
		current_tbs_id = DEFAULT_TBS_OID;
	} else if (in_dbname != NULL) {
		struct heap_tuple *tuple;
		Form_pg_database dbform;

		tuple = GetDatabaseTuple(in_dbname);
		if (!HT_VALID(tuple))
			ereport(FATAL, (
			errcode(E_UNDEFINED_DATABASE),
			errmsg("database \"%s\" does not exist", in_dbname)));

		dbform = (Form_pg_database) GET_STRUCT(tuple);
		current_db_id = HEAPTUP_OID(tuple);
		current_tbs_id = dbform->dattablespace;
		/* take database name from the caller, just for paranoia */
		strlcpy(dbname, in_dbname, sizeof(dbname));
	} else {
		/* caller specified database by OID */
		struct heap_tuple *tuple;
		Form_pg_database dbform;

		tuple = GetDatabaseTupleByOid(dboid);
		if (!HT_VALID(tuple)) {
			ereport(FATAL, (
			errcode(E_UNDEFINED_DATABASE),
			errmsg("database %u does not exist", dboid)));
		}

		dbform = (Form_pg_database) GET_STRUCT(tuple);
		current_db_id = HEAPTUP_OID(tuple);
		current_tbs_id = dbform->dattablespace;
		ASSERT(current_db_id == dboid);
		strlcpy(dbname, NAME_TO_STR(dbform->datname), sizeof(dbname));
		/* pass the database name back to the caller */
		if (out_dbname)
			strcpy(out_dbname, dbname);
	}

	/* Now we can mark our struct proc entry with the database ID */
	/* (We assume this is an atomic store so no lock is needed) */
	current_proc->databaseId = current_db_id;

	/*
	 * Now, take a writer's lock on the database we are trying to connect to.
	 * If there is a concurrently running DROP DATABASE on that database, this
	 * will block us until it finishes (and has committed its update of
	 * pg_database).
	 *
	 * Note that the lock is not held long, only until the end of this startup
	 * transaction.  This is OK since we are already advertising our use of
	 * the database in the struct proc array; anyone trying a DROP DATABASE after
	 * this point will see us there.
	 *
	 * Note: use of ROW_EXCL_LOCK here is reasonable because we envision
	 * our session as being a concurrent writer of the database.  If we had a
	 * way of declaring a session as being guaranteed-read-only, we could use
	 * ACCESS_SHR_LOCK for such sessions and thereby not conflict against
	 * CREATE DATABASE.
	 */
	if (!bootstrap)
		lock_sobj(DatabaseRelationId, current_db_id, 0, ROW_EXCL_LOCK);

	/*
	 * Recheck pg_database to make sure the target database hasn't gone away.
	 * If there was a concurrent DROP DATABASE, this ensures we will die
	 * cleanly without creating a mess.
	 */
	if (!bootstrap) {
		struct heap_tuple *tuple;

		tuple = GetDatabaseTuple(dbname);
		if (!HT_VALID(tuple) ||
			current_db_id != HEAPTUP_OID(tuple) ||
			current_tbs_id != ((Form_pg_database) GET_STRUCT(tuple))->dattablespace)
			ereport(FATAL, (
			errcode(E_UNDEFINED_DATABASE),
			errmsg("database \"%s\" does not exist", dbname),
			errdetail("It seems to have just been dropped or renamed.")));
	}

	/*
	 * Now we should be able to access the database directory safely. Verify
	 * it's there and looks reasonable.
	 */
	fullpath = get_db_path(current_db_id, current_tbs_id);

	if (!bootstrap) {
		if (access(fullpath, F_OK) == -1) {
			if (errno == ENOENT) {
				ereport(FATAL, (
				errcode(E_UNDEFINED_DATABASE),
				errmsg("database \"%s\" does not exist", dbname),
				errdetail("The database subdirectory \"%s\" is missing.", 
					fullpath)));
			} else {
				ereport(FATAL, (
				errcode_file_access(),
				errmsg("could not access directory \"%s\": %m", fullpath)));
			}
		}

		check_version(fullpath);
	}

	set_db_path(fullpath);

	/*
	 * It's now possible to do real access to the system catalogs.
	 *
	 * Load relcache entries for the system catalogs.  This must create at
	 * least the minimum set of "nailed-in" cache entries.
	 */
	relcache_init_phase3();

	/* set up ACL framework (so CheckMyDatabase can check permissions) */
	initialize_acl();

	/*
	 * Re-read the pg_database row for our database, check permissions and set
	 * up database-specific GUC settings.  We can't do this until all the
	 * database-access infrastructure is up.  (Also, it wants to know if the
	 * user is a superuser, so the above stuff has to happen first.)
	 */
	if (!bootstrap)
		CheckMyDatabase(dbname, am_superuser);

	/*
	 * Now process any command-line switches and any additional GUC variable
	 * settings passed in the startup packet.	We couldn't do this before
	 * because we didn't know if client is a superuser.
	 */
	if (proc_port != NULL)
		process_startup_options(proc_port, am_superuser);

	/* Process pg_db_role_setting options */
	process_settings(current_db_id, get_session_uid());

	/* Apply post_auth_delay as soon as we've read all options */
	if (post_auth_delay > 0)
		pg_usleep(post_auth_delay * 1000000L);

	/*
	 * Initialize various default states that can't be set up until we've
	 * selected the active user and gotten the right GUC settings.
	 */

	/* set default namespace search path */
	init_search_path();

	/* initialize client encoding */
	init_client_encoding();

	/* report this backend in the struct backend_status array */
	if (!bootstrap)
		stat_backend_start();

	/* close the transaction we started above */
	if (!bootstrap)
		commit_xact_cmd();
}
Beispiel #4
0
/* check filesystem against extra mtree data if available,
 * NOT guaranteed to catch db/filesystem discrepencies */
static int check_file_properties(alpm_pkg_t *pkg)
{
	char path[PATH_MAX], *rel;
	int ret = 0;
	size_t space;
	struct archive *mtree = alpm_pkg_mtree_open(pkg);
	struct archive_entry *entry;

	if(!mtree) {
		pu_ui_warn("%s: mtree data not available (%s)",
				alpm_pkg_get_name(pkg), strerror(errno));
		return require_mtree;
	}

	strncpy(path, alpm_option_get_root(handle), PATH_MAX);
	rel = path + strlen(path);
	space = PATH_MAX - (rel - path);

	while(alpm_pkg_mtree_next(pkg, mtree, &entry) == ARCHIVE_OK) {
		const char *ppath = archive_entry_pathname(entry);
		const char *fpath;
		struct stat buf;

		if(strncmp("./", ppath, 2) == 0) { ppath += 2; }

		if(strcmp(ppath, ".INSTALL") == 0) {
			if((fpath = get_db_path(pkg, "install")) == NULL) {
				continue;
			}
		} else if(strcmp(ppath, ".CHANGELOG") == 0) {
			if((fpath = get_db_path(pkg, "changelog")) == NULL) {
				continue;
			}
		} else if(ppath[0] == '.') {
			continue;
		} else if(skip_noextract && match_noextract(handle, ppath)) {
			continue;
		} else {
			strncpy(rel, ppath, space);
			fpath = path;
		}

		if(lstat(fpath, &buf) != 0) {
			if(errno == ENOENT) {
				eprintf("%s: '%s' missing file\n", alpm_pkg_get_name(pkg), fpath);
			} else {
				pu_ui_warn("%s: '%s' read error (%s)",
						alpm_pkg_get_name(pkg), fpath, strerror(errno));
			}
			ret = 1;
			continue;
		}

		if(cmp_type(pkg, fpath, entry, &buf) != 0) { ret = 1; }

		if(skip_noupgrade && match_noupgrade(handle, ppath)) { continue; }

		if(cmp_mode(pkg, fpath, entry, &buf) != 0) { ret = 1; }
		if(cmp_uid(pkg, fpath, entry, &buf) != 0) { ret = 1; }
		if(cmp_gid(pkg, fpath, entry, &buf) != 0) { ret = 1; }

		if(skip_backups && match_backup(pkg, ppath)) {
			continue;
		}

		if(S_ISLNK(buf.st_mode) && S_ISLNK(archive_entry_mode(entry))) {
			if(cmp_target(pkg, fpath, entry) != 0) { ret = 1; }
		}
		if(!S_ISDIR(buf.st_mode)) {
			if(cmp_mtime(pkg, fpath, entry, &buf) != 0) { ret = 1; }
			if(!S_ISLNK(buf.st_mode)) {
				/* always fails for directories and symlinks */
				if(cmp_size(pkg, fpath, entry, &buf) != 0) { ret = 1; }
			}
		}
	}
	alpm_pkg_mtree_close(pkg, mtree);

	if(!quiet && !ret) {
		eprintf("%s: all files match mtree\n", alpm_pkg_get_name(pkg));
	}

	return ret;
}
Beispiel #5
0
int main(int argc, char ** argv) {
	char * sep;
	sqlite3 * db = NULL;
	struct Package current = blank_package;
	char baseurl[256] = "";
	char line[sizeof(current.homepage)]; /* No line will be larger than the largest field */
	int code;
	int chained_call = 0;
	char * db_path = NULL;
	/* NOTE: If Package ever contains varible fields, this must be changed */
	char sql[sizeof(current) + 8*3*sizeof(char) + 137*sizeof(char) + 256*sizeof(char)];

	while((code = getopt(argc, argv, "schd:")) != -1) {
		switch(code) {
			case 's':
				if(db != NULL) {
					fputs("-d and -s are mutually exclusive\n", stderr);
					exit(EXIT_FAILURE);
				}
				print_sql = 1;
				break;
			case 'c':
				chained_call = 1;
				break;
			case 'h':
				help();
				break;
			case 'd':
				if(print_sql) {
					fputs("-d and -s are mutually exclusive\n", stderr);
					exit(EXIT_FAILURE);
				}
				if(sqlite3_open(optarg, &db) != 0) {
					fprintf(stderr, "%s\n", sqlite3_errmsg(db));
					exit(EXIT_FAILURE);
				}
				break;
			default:
				help();
		}
	}

	/* Open database */
	if(!print_sql && db == NULL && (db_path = get_db_path()) && sqlite3_open(db_path, &db) != 0) {
		fprintf(stderr, "%s\n", sqlite3_errmsg(db));
		exit(EXIT_FAILURE);
	}
	if(db_path) {
		free(db_path);
	}

	/* Do everything as one transaction. Many times faster */
	safe_execute(db, "BEGIN TRANSACTION;");

	/* Create tables if they do not exist */
	safe_execute(db, "CREATE TABLE IF NOT EXISTS packages " \
	                 "(package TEXT PRIMARY KEY, name TEXT, version TEXT," \
	                 "maintainer TEXT, installed_size INTEGER, size INTEGER," \
	                 "homepage TEXT, section TEXT, category TEXT, baseurl TEXT,"\
	                 "path TEXT, md5 TEXT, description TEXT, user_rating INTEGER,"\
	                 "user_owns TEXT, status INTEGER, rating INTEGER, price INTEGER);" \
	                 "CREATE TABLE IF NOT EXISTS virtual_packages (package TEXT PRIMARY KEY, is_really TEXT);" \
	                 "CREATE TABLE IF NOT EXISTS depends (package TEXT, depend TEXT, version TEXT);"
	            );

	if(!chained_call) {
		safe_execute(db, "DELETE FROM virtual_packages;");
		safe_execute(db, "DELETE FROM depends;");
	}

	/* Loop over lines from stream */
	code = 0;
	while(fgets(line, sizeof(line), stdin)) {
		if(line[0] == '#') {
			/* Chomp */
			if((sep = strchr(line, '\n'))) {
				*sep = '\0';
			}
			strncpy(baseurl, line + 1, sizeof(baseurl)-1);
		/* Blank line means end of this package definition */
		} else if(line[0] == '\n') {
			current.baseurl = baseurl;
			if(current.package[0] != '\0') {
				package_insert_sql(&current, sql, sizeof(sql));

				if(print_sql) {
					puts(sql);
				} else {
					if((code = sqlite3_exec(db, sql, NULL, NULL, NULL)) != 0) {
						if(code == SQLITE_CONSTRAINT) {
							package_update_sql(&current, sql, sizeof(sql));
							safe_execute(db, sql);
						} else {
							fprintf(stderr, "%s\n", sqlite3_errmsg(db));
							exit(EXIT_FAILURE);
						}
					}
				}
			}

			/* Reset things */
			code = 0;
			current = blank_package;
		} else {
			/* Chomp */
			if((sep = strchr(line, '\n'))) {
				*sep = '\0';
			}
			/* Description spans multiple lines at the end, concat stuff */
			if(code) {
				strncat(current.description, "\n", sizeof(current.description)-1);
				strncat(current.description, line, sizeof(current.description)-1);
			} else {
				/* Split on colon */
				if((sep = strchr(line, ':'))) {
					*sep = '\0';
					/* Skip over the space too */
					sep = sep + 2;
					/* If we haven't seen the field yet, do a string compare to see if
					 * this is it. Copy remainder of line into struct */
					if(       current.package[0]      == '\0' && strcmp(line, "Package")        == 0) {
						strncpy(current.package,     sep, sizeof(current.package)-1);
					} else if(current.name[0]         == '\0' && strcmp(line, "Name")           == 0) {
						strncpy(current.name,        sep, sizeof(current.name)-1);
					} else if(current.category[0]     == '\0' && strcmp(line, "Category")       == 0) {
						strncpy(current.category,    sep, sizeof(current.category)-1);
					} else if(current.version[0]      == '\0' && strcmp(line, "Version")        == 0) {
						strncpy(current.version,     sep, sizeof(current.version)-1);
					} else if(current.user_owns[0]    == '\0' && strcmp(line, "UserOwns")       == 0) {
						strncpy(current.user_owns,   sep, sizeof(current.user_owns)-1);
					} else if(current.section[0]      == '\0' && strcmp(line, "Section")        == 0) {
						strncpy(current.section,     sep, sizeof(current.section)-1);
					} else if(current.md5[0]          == '\0' && strcmp(line, "MD5sum")         == 0) {
						strncpy(current.md5,         sep, sizeof(current.md5)-1);
					} else if(current.maintainer[0]   == '\0' && strcmp(line, "Maintainer")     == 0) {
						strncpy(current.maintainer,  sep, sizeof(current.maintainer)-1);
					} else if(current.path[0]         == '\0' && strcmp(line, "Filename")       == 0) {
						strncat(current.path, sep,     sizeof(current.path)-1);
					} else if(current.homepage        == '\0' && strcmp(line, "Homepage")       == 0) {
						strncpy(current.homepage,    sep, sizeof(current.homepage)-1);
					} else if(current.rating          ==   0  && strcmp(line, "Rating")         == 0) {
						current.rating = atoi(sep);
					} else if(current.user_rating     ==   0  && strcmp(line, "UserRating")     == 0) {
						current.user_rating = atoi(sep);
					} else if(current.price          ==   0  && strcmp(line, "Price")           == 0) {
						current.price = atoi(sep);
					} else if(current.installed_size  ==   0  && strcmp(line, "Installed-Size") == 0) {
						current.installed_size = atoi(sep);
					} else if(current.size            ==   0  && strcmp(line, "Size")           == 0) {
						current.size = atoi(sep);
					} else if(                                   strcmp(line, "Provides")       == 0) {
						sep = strtok(sep, ", ");
						sql[0] = '\0';
						strncpy(sql, "INSERT INTO virtual_packages (package, is_really) VALUES(", sizeof(sql));
						quotecat(sql, sep, sizeof(sql), 1);
						quotecat(sql, current.package, sizeof(sql), 0);
						strncat(sql, ");", sizeof(sql));
						safe_execute(db, sql);
						while((sep = strtok(NULL, ", ")) != NULL) {
							sql[0] = '\0';
							strncpy(sql, "INSERT INTO virtual_packages (package, is_really) VALUES(", sizeof(sql));
							quotecat(sql, sep, sizeof(sql), 1);
							quotecat(sql, current.package, sizeof(sql), 0);
							strncat(sql, ");", sizeof(sql));
							safe_execute(db, sql);
						}
					} else if(                                   strcmp(line, "Depends")        == 0) {
						parse_depends(db, current.package, sep);
					} else if(                                   strcmp(line, "Description")    == 0) {
						strncpy(current.description, sep, sizeof(current.description)-1);
						code = 1;
					}
				}
			} /* if code */
		} /* if line[0] == '\n' */
	} /* while */

	/* End the transaction only when all data has been inserted */
	safe_execute(db, "END TRANSACTION;");

	/* Clean up disk space */
	if(!chained_call) {
		safe_execute(db, "DELETE FROM packages WHERE package='';");
		safe_execute(db, "VACUUM;");
	}

	/* Close database */
	if(db != NULL && sqlite3_close(db) != 0) {
		fprintf(stderr, "%s\n", sqlite3_errmsg(db));
		exit(EXIT_FAILURE);
	}

	exit(EXIT_SUCCESS);
}