375 lines
11 KiB
C
375 lines
11 KiB
C
/*
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"), to
|
|
deal in the Software without restriction, including without limitation the
|
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
sell copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
Original idea International Business Machines redook "Using ADSM to Back Up
|
|
Databases", published 1998.
|
|
|
|
Some adaption from HPC2N, Umeå University, Sweden, 2006/7
|
|
|
|
This version Copyright (c) 2012-2014 by Deon George
|
|
*/
|
|
|
|
/* Enable Large File Support stuff */
|
|
#define _FILE_OFFSET_BITS 64
|
|
#define _LARGEFILE_SOURCE 1
|
|
#define _LARGE_FILES 1
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <limits.h>
|
|
|
|
#include "dsmrc.h"
|
|
#include "dsmapitd.h"
|
|
#include "dsmapifp.h"
|
|
#include "tsmpipe.h"
|
|
|
|
int verbose=0;
|
|
|
|
int copy_env(const char *from, const char *to) {
|
|
char *e;
|
|
char n[PATH_MAX+1];
|
|
|
|
e = getenv(from);
|
|
if (!e) {
|
|
fprintf(stderr,"tsmpipe: Environment variable %s not set\n",from);
|
|
return 0;
|
|
}
|
|
|
|
n[PATH_MAX] = '\0';
|
|
snprintf(n, PATH_MAX, "%s=%s", to, e);
|
|
|
|
if (putenv(strdup(n))) {
|
|
perror("tsmpipe: Setting up environment");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void usage(void) {
|
|
fprintf(stderr,
|
|
"tsmpipe %s, usage:\n"
|
|
"\n"
|
|
"tsmpipe [-i]|[[-A|-B|-U] [-c|-x|-d|-t] -s fsname -f filepath [-l len] ...]\n"
|
|
" -i Show session information:\n"
|
|
" -A, -B and -U are mutually exclusive:\n"
|
|
" -A Use Archive objects\n"
|
|
" -B Use Backup objects\n"
|
|
" -U Update FS with utilisation info\n"
|
|
" -c, -x, -d and -t are mutually exclusive:\n"
|
|
" -c Create: Read from stdin and store in TSM\n"
|
|
" -x eXtract: Recall from TSM and write to stdout\n"
|
|
" -d Delete: Delete object from TSM\n"
|
|
" -t lisT: Print filelist to stdout\n"
|
|
" -s and -f are required arguments for (-A/ -B operations):\n"
|
|
" -s fsname Name of filespace in TSM\n"
|
|
" -f filepath Path to file within filesystem in TSM\n"
|
|
" -l length Length of object to store. If guesstimating, too large\n"
|
|
" is better than too small\n"
|
|
" -L length Length of object to store. But also update the FS with\n"
|
|
" the size of the object sent\n"
|
|
" -D desc Description of archive object\n"
|
|
" -P pitdate PITDate (mmddYYYY[:HHMMSS]) (BACKUP Objects)\n"
|
|
" -n insdate Insert Date lower bound (mmddYYYY[:HHMMSS]) (ARCHIVE Objects)\n"
|
|
" -N insdate Insert Date upper bound (mmddYYYY[:HHMMSS]) (ARCHIVE Objects)\n"
|
|
" -e expdate Expire Date lower bound (mmddYYYY[:HHMMSS]) (ARCHIVE Objects)\n"
|
|
" -E expdate Expire Date upper bound (mmddYYYY[:HHMMSS]) (ARCHIVE Objects)\n"
|
|
" -O options Extra options to pass to dsmInitEx\n"
|
|
#ifdef USE_DIGEST
|
|
" -m digest Calculate digest for data being stored in TSM, eg: md5, sha1...\n"
|
|
#endif
|
|
" -v Verbose. More -v's gives more verbosity\n"
|
|
" -V Verbose information on TSM transfer\n"
|
|
,_TSMPIPE_VERSION);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
void usage_action(void) {
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give one of -i, -c, -x, -d, -t\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int c;
|
|
extern int optopt;
|
|
extern char *optarg;
|
|
int action=0;
|
|
char *space=NULL, *filename=NULL, *lenstr=NULL, *desc=NULL, *pitdate=NULL, *options=NULL, *expdate_low=NULL, *expdate_high=NULL, *insdate_low=NULL, *insdate_high=NULL;
|
|
dsBool_t summary=bFalse;
|
|
off_t length;
|
|
dsUint32_t dsmHandle;
|
|
dsmQueryType qType=0xff; // We set this to an unlikely value, it should be set correctly via our getopt
|
|
qryArchiveData qaData;
|
|
qryBackupData qbData;
|
|
dsmObjName objName;
|
|
int rc=0;
|
|
double stored=0;
|
|
#ifdef USE_DIGEST
|
|
char *digest=NULL;
|
|
#endif
|
|
|
|
while ((c = getopt(argc, argv, "hiABcxdtUvVe:E:f:l:L:m:n:N:s:D:O:P:")) != -1) {
|
|
switch(c) {
|
|
case 'h': usage(); break;
|
|
case 'A': qType = qtArchive; break;
|
|
case 'B': qType = qtBackup; break;
|
|
|
|
case 'i': if (action != 0) usage_action(); action = ACTION_INFO; break;
|
|
case 'c': if (action != 0) usage_action(); action = ACTION_CREATE; break;
|
|
case 'x': if (action != 0) usage_action(); action = ACTION_EXTRACT; break;
|
|
case 'd': if (action != 0) usage_action(); action = ACTION_DELETE; break;
|
|
case 't': if (action != 0) usage_action(); action = ACTION_LIST; break;
|
|
case 'U': if (action != 0) usage_action(); action = ACTION_UPDATE; break;
|
|
|
|
case 'v': verbose++; break;
|
|
case 'V': summary = bTrue; break;
|
|
case 's': space = optarg; break;
|
|
case 'f': filename = optarg; break;
|
|
case 'l': lenstr = optarg; break;
|
|
case 'L': lenstr = optarg; if (action != ACTION_CREATE) usage_action(); action = ACTION_CREATE_UPDATE; break;
|
|
case 'D': desc = optarg; break;
|
|
|
|
#ifdef USE_DIGEST
|
|
case 'm': digest = optarg; break;
|
|
#endif
|
|
case 'O': options = optarg; break;
|
|
|
|
case 'P': pitdate = optarg; break;
|
|
case 'e': expdate_low = optarg; break;
|
|
case 'E': expdate_high = optarg; break;
|
|
case 'n': insdate_low = optarg; break;
|
|
case 'N': insdate_high = optarg; break;
|
|
|
|
case ':':
|
|
fprintf(stderr, "tsmpipe: Option -%c requires an operand\n", optopt);
|
|
exit(1);
|
|
case '?':
|
|
fprintf(stderr, "tsmpipe: Unrecognized option: -%c\n", optopt);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// Arguement Validation
|
|
if (action == 0) {
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give one of -i, -c, -x, -d, -t\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (pitdate && qType != qtBackup) {
|
|
fprintf(stderr, "tsmpipe: ERROR: -P can only be used with -B\n");
|
|
exit(1);
|
|
}
|
|
|
|
if ((insdate_low|| insdate_high || expdate_low || expdate_high) && qType != qtArchive) {
|
|
fprintf(stderr, "tsmpipe: ERROR: -e, -E, -n, -N can only be used with -A\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (action != ACTION_INFO) {
|
|
if (! space) {
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give -s filespacename\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (! filename && action != ACTION_UPDATE) {
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give -f filename\n");
|
|
exit(1);
|
|
}
|
|
|
|
if ((action == ACTION_CREATE || action == ACTION_CREATE_UPDATE || action == ACTION_UPDATE) && ! lenstr) {
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give -l|-L length with -c | -u\n");
|
|
exit(1);
|
|
}
|
|
|
|
if ((action != ACTION_CREATE && action != ACTION_CREATE_UPDATE && action != ACTION_UPDATE) && lenstr) {
|
|
fprintf(stderr, "tsmpipe: ERROR: -l|-L length useless without -c | -u\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (desc && qType != qtArchive) {
|
|
fprintf(stderr, "tsmpipe: ERROR: -D desc useless without -A\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* Let the TSM api get the signals */
|
|
signal(SIGPIPE, SIG_IGN);
|
|
signal(SIGINT, SIG_IGN);
|
|
signal(SIGUSR1, SIG_IGN);
|
|
|
|
if (getenv("DSM_DIR") && ((! copy_env("DSM_DIR", "DSMI_DIR")) || (! copy_env("DSM_CONFIG", "DSMI_CONFIG"))))
|
|
exit(1);
|
|
|
|
// OK, we are ready to talk to TSM
|
|
debugLog(1,stderr,"tsmpipe: Create TSM session",0);
|
|
|
|
dsmHandle = tsm_initsess(options);
|
|
if (! dsmHandle)
|
|
debugLog(0,stderr,"tsmpipe: Unable to create TSM session?",2);
|
|
|
|
debugLog(2,stderr,"tsmpipe: Session Initiated",0);
|
|
|
|
switch (action) {
|
|
// Show our session information
|
|
case ACTION_INFO:
|
|
debugLog(2,stderr,"tsmpipe: INFO Operation",0);
|
|
|
|
rc = tsm_sessioninfo(dsmHandle);
|
|
break;
|
|
|
|
// If we are backing up or archiving
|
|
case ACTION_CREATE:
|
|
case ACTION_CREATE_UPDATE:
|
|
debugLog(2,stderr,"tsmpipe: CREATE Operation",0);
|
|
|
|
length = atof(lenstr);
|
|
if (length <= 0)
|
|
debugLog(0,stderr,"tsmpipe: ERROR: Provide positive length, overestimate if guessing",1);
|
|
|
|
#ifdef USE_DIGEST
|
|
stored = tsm_sendfile(dsmHandle,space,filename,length,desc,((qType == qtArchive) ? stArchiveMountWait : stBackupMountWait),((action == ACTION_CREATE_UPDATE && qType==qtBackup) ? bTrue : bFalse),summary,digest);
|
|
#else
|
|
stored = tsm_sendfile(dsmHandle,space,filename,length,desc,((qType == qtArchive) ? stArchiveMountWait : stBackupMountWait),((action == ACTION_CREATE_UPDATE && qType==qtBackup) ? bTrue : bFalse),summary);
|
|
#endif
|
|
rc = stored ? 1 : 0;
|
|
|
|
if (rc && action == ACTION_CREATE_UPDATE && qType==qtBackup) {
|
|
debugLog(2,stderr,"tsmpipe: UPDATE FS Operation",0);
|
|
rc = tsm_updatefs(dsmHandle,space,stored);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACTION_DELETE:
|
|
case ACTION_LIST:
|
|
case ACTION_EXTRACT:
|
|
memset(&qaData,0x00,sizeof(qryArchiveData));
|
|
memset(&qbData,0x00,sizeof(qryBackupData));
|
|
objName = dsmNameToObjname(space,filename);
|
|
|
|
// Setup our Query Object
|
|
switch (qType) {
|
|
case qtBackup:
|
|
qbData.stVersion = qryBackupDataVersion;
|
|
qbData.objName = &objName;
|
|
qbData.owner = "";
|
|
|
|
switch (action) {
|
|
case ACTION_DELETE:
|
|
qbData.objState = DSM_ACTIVE;
|
|
break;
|
|
|
|
case ACTION_EXTRACT:
|
|
qbData.objState = pitdate ? DSM_ANY_MATCH : DSM_ACTIVE;
|
|
break;
|
|
|
|
case ACTION_LIST:
|
|
qbData.objState = DSM_ANY_MATCH;
|
|
break;
|
|
}
|
|
|
|
if (action == ACTION_DELETE || ! pitdate) {
|
|
qbData.pitDate.year = DATE_MINUS_INFINITE;
|
|
} else {
|
|
qbData.pitDate = dsmStrToDate(pitdate);
|
|
}
|
|
|
|
break;
|
|
|
|
case qtArchive:
|
|
qaData.stVersion = qryArchiveDataVersion;
|
|
qaData.objName = &objName;
|
|
qaData.owner = "";
|
|
qaData.descr = desc ? desc : "*";
|
|
|
|
qaData.insDateLowerBound.year = DATE_MINUS_INFINITE;
|
|
qaData.insDateUpperBound.year = DATE_PLUS_INFINITE;
|
|
qaData.expDateLowerBound.year = DATE_MINUS_INFINITE;
|
|
qaData.expDateUpperBound.year = DATE_PLUS_INFINITE;
|
|
|
|
if (insdate_low)
|
|
qaData.insDateLowerBound = dsmStrToDate(insdate_low);
|
|
|
|
if (insdate_high)
|
|
qaData.insDateUpperBound = dsmStrToDate(insdate_high);
|
|
|
|
if (expdate_low)
|
|
qaData.expDateLowerBound = dsmStrToDate(expdate_low);
|
|
|
|
if (expdate_high)
|
|
qaData.expDateUpperBound = dsmStrToDate(expdate_high);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,"tsmpipe: UNKNOWN Type %d",qType);
|
|
exit(2);
|
|
}
|
|
|
|
switch (action) {
|
|
case ACTION_DELETE:
|
|
debugLog(2,stderr,"tsmpipe: DELETE Operation",0);
|
|
rc = tsm_deletefile(dsmHandle,qType,qaData,qbData);
|
|
break;
|
|
|
|
case ACTION_EXTRACT:
|
|
debugLog(2,stderr,"tsmpipe: EXTRACT Operation",0);
|
|
rc = tsm_restorefile(dsmHandle,qType,qaData,qbData);
|
|
break;
|
|
|
|
case ACTION_LIST:
|
|
debugLog(2,stderr,"tsmpipe: LIST Operation",0);
|
|
rc = tsm_listfile(dsmHandle,qType,qaData,qbData);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,"tsmpipe: Action not yet programmed for%d",action);
|
|
exit(2);
|
|
}
|
|
break;
|
|
|
|
case ACTION_UPDATE:
|
|
length = atof(lenstr);
|
|
if (length <= 0)
|
|
debugLog(0,stderr,"tsmpipe: ERROR: Provide positive length",1);
|
|
|
|
rc = tsm_updatefs(dsmHandle,space,length);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,"tsmpipe: UNKNOWN Operation %d",action);
|
|
exit(2);
|
|
}
|
|
|
|
debugLog(1,stderr,"tsmpipe: Terminate TSM session",0);
|
|
|
|
dsmTerminate(dsmHandle);
|
|
|
|
if (! rc)
|
|
debugLog(0,stderr,"tsmpipe: Operation Failed",3);
|
|
else
|
|
debugLog(1,stderr,"tsmpipe: Operation Success",0);
|
|
|
|
return 0;
|
|
}
|