2010-11-14 04:13:57 +00:00
|
|
|
|
/*
|
|
|
|
|
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
|
2013-07-18 13:59:14 +00:00
|
|
|
|
SOFTWARE.
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
Original Idea Copyright (c) 2006,2007 HPC2N, Ume<EFBFBD> University, Sweden
|
|
|
|
|
Modifications Copyright (c) 2012-2013 by Deon George
|
2013-07-15 23:58:53 +00:00
|
|
|
|
*/
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
|
|
|
|
/* 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"
|
2013-11-05 05:11:45 +00:00
|
|
|
|
#include "tsmpipe.h"
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
int verbose=0;
|
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
int copy_env(const char *from, const char *to) {
|
|
|
|
|
char *e;
|
|
|
|
|
char n[PATH_MAX+1];
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
e = getenv(from);
|
|
|
|
|
if (!e) {
|
|
|
|
|
fprintf(stderr,"tsmpipe: Environment variable %s not set\n",from);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
n[PATH_MAX] = '\0';
|
|
|
|
|
snprintf(n, PATH_MAX, "%s=%s", to, e);
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
if (putenv(strdup(n))) {
|
|
|
|
|
perror("tsmpipe: Setting up environment");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-11-14 04:13:57 +00:00
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
return 1;
|
2010-11-14 04:13:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void usage(void) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
fprintf(stderr,
|
2013-11-05 05:11:45 +00:00
|
|
|
|
"tsmpipe %s, usage:\n"
|
2013-07-18 13:59:14 +00:00
|
|
|
|
"\n"
|
2013-11-07 07:06:56 +00:00
|
|
|
|
"tsmpipe [-i]|[[-A|-B] [-c|-x|-d|-t] -s fsname -f filepath [-l len] ...]\n"
|
2013-07-18 13:59:14 +00:00
|
|
|
|
" -i Show session information:\n"
|
|
|
|
|
" -A and -B are mutually exclusive:\n"
|
|
|
|
|
" -A Use Archive objects\n"
|
|
|
|
|
" -B Use Backup objects\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 filesystem 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"
|
|
|
|
|
" -D desc Description of archive object\n"
|
2013-11-07 04:10:18 +00:00
|
|
|
|
" -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"
|
2013-07-18 13:59:14 +00:00
|
|
|
|
" -O options Extra options to pass to dsmInitEx\n"
|
2013-11-07 07:06:56 +00:00
|
|
|
|
#ifdef USE_DIGEST
|
|
|
|
|
" -m digest Calculate digest for data being stored in TSM, eg: md5, sha1, sha512..."
|
|
|
|
|
#endif
|
2013-07-18 13:59:14 +00:00
|
|
|
|
" -v Verbose. More -v's gives more verbosity\n"
|
2013-11-05 05:11:45 +00:00
|
|
|
|
,_TSMPIPE_VERSION);
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
|
|
|
|
exit(1);
|
2010-11-14 04:13:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
void usage_action(void) {
|
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give one of -i, -c, -x, -d, -t\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-11-14 04:13:57 +00:00
|
|
|
|
int main(int argc, char *argv[]) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
int c;
|
|
|
|
|
extern int optopt;
|
|
|
|
|
extern char *optarg;
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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;
|
2013-07-18 13:59:14 +00:00
|
|
|
|
off_t length;
|
|
|
|
|
dsUint32_t dsmHandle;
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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;
|
2013-11-07 07:06:56 +00:00
|
|
|
|
#ifdef USE_DIGEST
|
|
|
|
|
char *digest=NULL;
|
|
|
|
|
#endif
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
2013-11-07 07:06:56 +00:00
|
|
|
|
while ((c = getopt(argc, argv, "hiABcxdtve:E:f:l:m:n:N:s:D:O:P:")) != -1) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
switch(c) {
|
|
|
|
|
case 'h': usage(); break;
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
case 'v': verbose++; break;
|
|
|
|
|
case 's': space = optarg; break;
|
|
|
|
|
case 'f': filename = optarg; break;
|
|
|
|
|
case 'l': lenstr = optarg; break;
|
|
|
|
|
case 'D': desc = optarg; break;
|
2013-11-07 04:10:18 +00:00
|
|
|
|
|
2013-11-07 07:06:56 +00:00
|
|
|
|
#ifdef USE_DIGEST
|
|
|
|
|
case 'm': digest = optarg; break;
|
|
|
|
|
#endif
|
2013-07-18 13:59:14 +00:00
|
|
|
|
case 'O': options = optarg; break;
|
2013-11-07 04:10:18 +00:00
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
case 'P': pitdate = optarg; break;
|
2013-11-07 04:10:18 +00:00
|
|
|
|
case 'e': expdate_low = optarg; break;
|
|
|
|
|
case 'E': expdate_high = optarg; break;
|
|
|
|
|
case 'n': insdate_low = optarg; break;
|
|
|
|
|
case 'N': insdate_high = optarg; break;
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
|
|
|
|
case ':':
|
|
|
|
|
fprintf(stderr, "tsmpipe: Option -%c requires an operand\n", optopt);
|
|
|
|
|
exit(1);
|
|
|
|
|
case '?':
|
|
|
|
|
fprintf(stderr, "tsmpipe: Unrecognized option: -%c\n", optopt);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
// Arguement Validation
|
|
|
|
|
if (action == 0) {
|
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give one of -i, -c, -x, -d, -t\n");
|
2013-07-18 13:59:14 +00:00
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (pitdate && qType != qtBackup) {
|
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: -P can only be used with -B\n");
|
2013-11-04 13:13:12 +00:00
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (action != ACTION_INFO) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
if (! space) {
|
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give -s filespacename\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! filename) {
|
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give -f filename\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (action == ACTION_CREATE && ! lenstr) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: Must give -l length with -c\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (action != ACTION_CREATE && lenstr) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
fprintf(stderr, "tsmpipe: ERROR: -l length useless without -c\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (desc && qType != qtArchive) {
|
2013-07-18 13:59:14 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (getenv("DSM_DIR") && ((! copy_env("DSM_DIR", "DSMI_DIR")) || (! copy_env("DSM_CONFIG", "DSMI_CONFIG"))))
|
2013-07-18 13:59:14 +00:00
|
|
|
|
exit(1);
|
|
|
|
|
|
|
|
|
|
// OK, we are ready to talk to TSM
|
2013-11-07 04:10:18 +00:00
|
|
|
|
debugLog(1,stderr,"tsmpipe: Create TSM session",0);
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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:
|
|
|
|
|
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);
|
|
|
|
|
|
2013-11-07 07:06:56 +00:00
|
|
|
|
#ifdef USE_DIGEST
|
|
|
|
|
rc = tsm_sendfile(dsmHandle,space,filename,length,desc,((qType == qtArchive) ? stArchiveMountWait : stBackupMountWait),digest);
|
|
|
|
|
#else
|
2013-11-07 04:10:18 +00:00
|
|
|
|
rc = tsm_sendfile(dsmHandle,space,filename,length,desc,((qType == qtArchive) ? stArchiveMountWait : stBackupMountWait));
|
2013-11-07 07:06:56 +00:00
|
|
|
|
#endif
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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 = "";
|
2013-11-11 02:49:46 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2013-11-07 04:10:18 +00:00
|
|
|
|
|
|
|
|
|
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 : "*";
|
2013-11-05 05:11:45 +00:00
|
|
|
|
|
|
|
|
|
qaData.insDateLowerBound.year = DATE_MINUS_INFINITE;
|
|
|
|
|
qaData.insDateUpperBound.year = DATE_PLUS_INFINITE;
|
|
|
|
|
qaData.expDateLowerBound.year = DATE_MINUS_INFINITE;
|
|
|
|
|
qaData.expDateUpperBound.year = DATE_PLUS_INFINITE;
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (insdate_low)
|
|
|
|
|
qaData.insDateLowerBound = dsmStrToDate(insdate_low);
|
2013-11-05 05:11:45 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (insdate_high)
|
|
|
|
|
qaData.insDateUpperBound = dsmStrToDate(insdate_high);
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (expdate_low)
|
|
|
|
|
qaData.expDateLowerBound = dsmStrToDate(expdate_low);
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (expdate_high)
|
|
|
|
|
qaData.expDateUpperBound = dsmStrToDate(expdate_high);
|
|
|
|
|
break;
|
2013-11-05 05:11:45 +00:00
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr,"tsmpipe: UNKNOWN Type %d",qType);
|
|
|
|
|
exit(2);
|
2013-11-05 05:11:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
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);
|
2013-11-05 05:11:45 +00:00
|
|
|
|
}
|
2013-11-07 04:10:18 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr,"tsmpipe: UNKNOWN Operation %d",action);
|
|
|
|
|
exit(2);
|
2013-07-18 13:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
debugLog(1,stderr,"tsmpipe: Terminate TSM session",0);
|
|
|
|
|
|
2013-07-18 13:59:14 +00:00
|
|
|
|
dsmTerminate(dsmHandle);
|
|
|
|
|
|
2013-11-07 04:10:18 +00:00
|
|
|
|
if (! rc)
|
|
|
|
|
debugLog(0,stderr,"tsmpipe: Operation Failed",3);
|
|
|
|
|
else
|
|
|
|
|
debugLog(1,stderr,"tsmpipe: Operation Success",0);
|
2013-07-18 13:59:14 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
2010-11-14 04:13:57 +00:00
|
|
|
|
}
|