nisbackup.cc revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*
* Copyright (c) 1988-1997 Sun Microsystems Inc
* All Rights Reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* A utility for performing database backups.
*/
#include <stdio.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#include <stdlib.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <rpcsvc/nis_tags.h>
#include "nis_bkrst.h"
#include "../rpc.nisd/nis_proc.h"
/*
* Global state variables.
*/
static int flags = 0;
static char *nis_local_dir = __nis_rpc_domain();
static char *backupdir;
static directory_obj slist;
static void set_rw(void);
static void abort_backup(char *);
static void abort_handler(int);
/*
* Needs to be cleaned up in nislog as well as here. nis_log_common.o
* is shared by rpc.nisd, nislog and nisbackup/nisrestore. map_log()
* calls abort_transaction, which is defined in nis_svc_log.c. We can't
* link with nis_log_svc, so we'll stub it here. It's not used, since
* we are not modifying the transaction log.
*/
int
abort_transaction(int xid)
{
xid = 0;
return (0);
}
static void
{
}
static void
abort_handler(int sig)
{
sig = 0;
exit(1);
}
void
usage()
{
exit(1);
}
struct file_set {
char **fs;
};
void
{
exit(1);
}
sizeof (char *))) == NULL) {
exit(1);
}
exit(1);
}
}
}
/*
* This routine sets the rpc.nisd into a read-only state, so that the
* nisbackup can take a snapshot of the database. We set a global (server_ro)
* to indicate if we're sucessful. set_rw() is called on a kill signal (^C)
* and we don't want to reset the server to read-write if we're being aborted
* due to the existance of another nisbackup in progress!
*/
static nis_error
set_ro(void)
{
} else {
if (error == NIS_SUCCESS)
}
}
return (error);
}
static void
set_rw(void)
{
/*
* If we never set the server to read-only, we don't want to try
* to set it to read-write. This could cause another instance of
* nisbackup to fail.
*/
return;
if (error != NIS_SUCCESS) {
nis_sperrno(error));
}
}
#define PRESERVE 0
#define RESTORE 1
int
{
struct stat s;
} else {
}
"Directory %s does not exist: %m", src);
return (1);
}
case -1: /* error */
return (1);
case 0: /* child */
_exit(1);
_exit(0); /* Since this is the child proc, we'll exit */
default: /* parent, we'll wait for the cp to complete */
src);
return (1);
} else {
"Can't move directory: error = %d",
return (1);
}
}
}
return (0);
}
int
{
struct stat s;
"nisbackup: directory %s does not exist: %m",
name);
return (1);
}
case -1: /* error */
return (1);
case 0: /* child */
"-r",
name,
NULL) < 0)
_exit(1);
_exit(0); /* Since this is the child proc, we'll exit */
default: /* parent, we'll wait for the cp to complete */
return (1);
} else {
"Can't remove files: error = %d",
return (1);
}
}
}
return (0);
}
/*
* bool_t is_master_of()
*
* Boolean function to check if local host is the master for the object
* passed in nis_obj. If the passed in object is not fully qualified, then
* nis_getnames() is called to resolve the object's full name. The parameter,
* full_name, is used to return the fully qualified name (ignored if set to
* NULL). The serving list is returned in srv_list.
*/
{
char myname[NIS_MAXNAMELEN];
int i;
/*
* We will use __nis_CacheBind instead of nis_lookup() so that we
* can take advantage of the NIS_SHARED_DIRCACHE.
*/
for (i = 0; namelist[i]; i++) {
#ifdef DEBUG
#endif
if (err == NIS_SUCCESS) {
if (full_name)
break;
}
}
} else { /* Fully qualified */
if (err != NIS_SUCCESS) {
if (verbiage)
"Unable to lookup %s: %s", nis_obj,
nis_sperrno(err));
return (0);
}
if (full_name)
}
if (err != NIS_SUCCESS) {
if (verbiage)
return (0);
}
/*
* If we can get a directory object, we check the master server name
* against the local host's name. This is the final result.
*/
if (err == NIS_SUCCESS) {
if (strcasecmp(myname,
== 0) {
return (1);
}
}
return (0);
}
/*
* This is a link list of all the directories backed up on this server.
*/
struct nis_dir_list {
char *name;
struct nis_dir_list *next;
};
/*
* dirobj_list() maintains list of directory objects that are backed up
* with the -a(ll) option of the nisbackup utility.
*
* The fclose() calls are checked for error returns, _except_ in the case
* where an error has already occured, and we're returning FALSE.
*/
struct nis_dir_list *
{
int ss;
switch (op) {
case CACHE_INIT:
/* Initialize cache with the serving list, for -a(ll) */
filename);
return (NULL);
}
}
name++;
end++;
tmp = (struct nis_dir_list *)
XMALLOC(sizeof (struct nis_dir_list));
"Internal error, no memory: %m");
exit(1);
}
/*
* If we're the master, and it hasn't already been added...
*/
(char *)tmpname) == 0) {
break;
}
continue;
"Internal error, no memory: %m");
exit(1);
}
dirlisthead = tmp;
} else {
#ifdef DEBUG
#endif
}
}
exit(1);
}
return (dirlisthead);
case CACHE_ADD:
return (NULL);
/* Check whether directory already added */
return (tmp);
/* Add it to the cache */
tmp = (struct nis_dir_list *)
XMALLOC(sizeof (struct nis_dir_list));
return (NULL);
}
return (NULL);
}
dirlisthead = tmp;
#ifdef DEBUG
#endif
return (tmp);
case CACHE_SAVE:
/*
* Save back'd up object(s) to a file. If argp set, then we're
* doing an individual object to the file:
* /backupdir/nis+object/backup_list. If argp is NULL, we're
* doing the whole cached list in /backupdir/backup_list.
*/
return (NULL);
}
"Could not open file %s for updating: %m",
argp);
return (NULL);
}
}
return (NULL);
return (dirlisthead);
case CACHE_FIND: /* Search for object in cache */
return (NULL);
return (tmp);
return (NULL);
default:
return (NULL);
}
}
void
abort_backup(char * backupdir)
{
char buf[NIS_MAXNAMELEN];
struct stat s;
if (!backup_preserved) {
/*
* If we didn't save an existing backup, then we didn't create anything
* either, so there's nothing to cleanup.
*/
return;
}
if (verbiage)
"Aborting NIS+ directory backup. Cleaning up files.");
/*
*/
if (file_cleanup) {
"Unable to remove directory %s", buf);
}
} else {
"Unable to stat file system directory %s: %m",
buf);
}
}
}
if (backup_preserved) {
"Unable to restore old backup %s.tmp",
buf);
}
}
}
}
void
{
char buf[NIS_MAXNAMELEN];
struct stat s;
/*
* Check if preserved backup directories exists. If yes, delete them.
*/
"Unable to remove old backup %s", buf);
}
}
}
}
{
char buffer[4096];
ssize_t n;
return (FALSE);
return (FALSE);
}
if (n == -1) {
return (FALSE);
}
return (FALSE);
}
}
return (FALSE);
return (FALSE);
return (TRUE);
}
{
char logfile[NIS_MAXNAMELEN];
struct stat s;
char *fs_p;
char **fs;
int i;
if (verbiage)
for (i = 0; i < fslen; ++i) {
/*
* Simple copy of file.
*/
return (FALSE);
} else {
/*
* This loads the log file (if there is one) and copies all data
* to backup file. This esentially checkpoints the data file.
*/
return (FALSE);
}
}
/*
* Finally, some special case files that need to be included, but that
* aren't part of the data dictionary.
*/
}
return (TRUE);
}
/*
* bool_t is_this_a_dir()
*
* Boolean function to check if the object passed in is a directory object,
* as opposed to a table object. This routine uses nis_lookup(), in nis_db.c,
* to get a nis_object structure, which identifies the object type.
*/
{
char fullname[NIS_MAXNAMELEN];
#ifdef DEBUG
#endif
return (FALSE);
#ifdef DEBUG
#endif
return (FALSE);
}
#ifdef DEBUG
#endif
return (TRUE);
else
return (FALSE);
}
return (FALSE);
}
void
sanity_checks(char *backupdir)
{
struct stat s;
char buf[NIS_MAXNAMELEN];
exit(1);
}
"Directory %s does not exist, please create.",
exit(1);
} else {
"An error occured while accessing %s: %m", backupdir);
exit(1);
}
}
/*
* Are we backing up a root-domain ? Note, either the root object
* has been specified, or the -a(ll) option. If so, check for the
* existence of a root object.
*/
(flags & DOMAIN_ALL)) {
}
else
}
/*
* We can initialize the object cache with the serving list if we
* are backing up -a(ll). If the directory object(s) to backup is
* specified (not -a(ll) option), add that in to initialize the cache.
*/
if (flags & DOMAIN_ALL) {
"Could not initialize, is this a master server?");
exit(1);
}
/*
* The cache is initialized, now we need initialize the server
* list.
*/
exit(1);
}
}
#ifdef DEBUG
#endif
/*
* Backup dirs setup, initialize signal handlers, which will cleanup
* if nisbackup is interupted from this point.
*/
init_signals();
/*
* Make sure we reset the read-write flag in the server upon exit.
* set_rw() checks server_ro == TRUE, to make sure we actually set
* it to read-only!
*/
/*
* Set server state to read-only. This must be done to take a backup.
*/
int slp_time = 2;
int retries = 0;
(retries++ < MAXRETRIES)) {
if (verbiage) {
"Unable to set server to read-only: %s",
nis_sperrno(error));
}
}
if (error != NIS_SUCCESS) {
"Unable to set server to read-only mode, exiting.");
exit(1);
}
/*
* Check if backup directory(ies) exists. If not, create. If they
* do exist (old backup), save them off, until new backup created.
* Upon successful creation of a new backup, remove the saved copy.
*/
"Unable to create directory %s: %m",
buf);
exit(1);
}
} else {
"Unable to stat file system directory %s: %m",
buf);
exit(1);
}
} else {
/*
* Move old backup to backup-dir.tmp. Once backup
* successful, backup-dir.tmp will be removed. In
* case of failure, we will not lose old backup.
*/
"Unable to save old backup %s", buf);
exit(1);
}
"Unable to create directory %s: %m",
buf);
exit(1);
}
file_cleanup = TRUE;
}
/*
* Check for a "data" directory under each backup-dir.
*/
"Unable to create directory %s: %m",
buf);
exit(1);
}
} else {
"Unable to stat directory %s: %m", buf);
exit(1);
}
}
}
}
void
{
char localdir[NIS_MAXNAMELEN];
char full_name[NIS_MAXNAMELEN];
char buf[NIS_MAXNAMELEN];
char *domainof;
exit(1);
}
/* This assumes that the dictionary has been initialized */
continue; /* Ignore */
continue; /* Ignore */
continue; /* Ignore log files */
continue; /* Ignore root.object, we'll get it later */
#ifdef DEBUG
#endif
/*
* Look up the file in the data dictionary. It could be a stray ufs
* file, that has nothing to do with NIS+.
*/
/*
* Check for the directory objects that are being backed up. Put them into
* their own fileset, instead of the directory object that they belong
* to. This insures that each object backup is autonomous, and can be restored
* separately. Is it a DIRECTORY? Is it in list of objects to backup? fspush it!
*/
== NULL)
continue;
} else {
/*
* Determine the which directory object this object belongs to.
*/
continue;
}
continue;
} /* In data dictionary */
} /* while (readdir) */
}
{
char fullname[NIS_MAXNAMELEN];
int ss;
int i;
"Cannot open file %s for updating: %m", newlog);
return (FALSE);
}
}
/*
* Write a backup revision number to the this file, so that nisrestore(1M)
* will know if it can understand this backup.
*/
return (FALSE);
}
/*
* Find the last time stamp for the directory(ies) being modified.
*/
return (FALSE);
}
/*
* Since, we are running as root, the nis_local_principal() should be OK.
*/
for (i = 0; i < MAXRETRIES; i++)
fullname)) != 0)
break;
if (u_time == 0) {
fullname);
return (FALSE);
}
return (FALSE);
}
#ifdef DEBUG
#endif
return (FALSE);
}
return (TRUE);
}
{
int ss;
/*
* If it doesn't exist, CACHE_SAVE will create it.
*/
} else {
return (FALSE);
name++;
end++;
return (FALSE);
}
}
}
}
int
{
char nisdomain[NIS_MAXNAMELEN];
char buf[NIS_MAXNAMELEN];
int c;
char *tmp;
if (geteuid() != 0) {
exit(1);
}
if (argc < 3)
usage();
switch (c) {
case 'a' :
flags |= DOMAIN_ALL;
break;
case 'v' :
break;
default:
usage();
}
}
if (flags & DOMAIN_ALL) {
usage();
} else {
usage();
/*
* Yes, I really mean "=", not "==". tmp is assigned in while.
*/
== NULL) {
"Internal error, unable to add to cache");
exit(1);
}
} else {
exit(1);
}
}
}
if (dbstat == 0) {
exit(1);
}
/*
* Loop through dirobj_list, creating the backup_path for each
* object in the cache, then db_extrace_dict_entries, backup,
* create_perdir_translog and write backup_list for each entry in the cache.
*/
exit(1);
}
exit(1);
}
exit(1);
}
}
if (!merge_backup_list()) {
exit(1);
}
exit(0);
}