This file is indexed.

/usr/lib/amanda/chg-zd-mtx is in amanda-server 1:3.3.9-5.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
#!/bin/sh
#
# Exit Status:
# 0 Alles Ok
# 1 Illegal Request
# 2 Fatal Error
#
# Contributed by Eric DOUTRELEAU <Eric.Doutreleau@int-evry.fr>
# This is supposed to work with Zubkoff/Dandelion version of mtx
#
# Modified by Joe Rhett <jrhett@isite.net>
# to work with MTX 1.2.9 by Eric Lee Green http://mtx.sourceforge.net
#
# Modified by Jason Hollinden <jhollind@sammg.com> on 13-Feb-2001
# to work with MTX 1.2.10, >9 slots, has barcode support, and works with
# multiple configs at once.
# NOTE:  Only tested the 2 additions with an ADIC Scalar 100.

################################################################################
# Here are the things you need to do and know to configure this script:
#
#   * Figure out what the robot device name is and what the tape drive
#     device name is.  They will be different!
#
#     You cannot send robot commands to a tape drive and vice versa.
#     Both should respond to "mtx -f /dev/... inquiry".  Hopefully,
#     that output will make it obvious which is which.
#
#     For instance, here is what mtx has to say about my current robot:
#
#       Product Type: Medium Changer
#       Vendor ID: 'ATL     '
#       Product ID: 'ACL2640 206     '
#       Revision: '2A5A'
#       Attached Changer: No
#
#     and here is what it says about a tape drive:
#
#       Product Type: Tape Drive
#       Vendor ID: 'Quantum '
#       Product ID: 'DLT4000         '
#       Revision: 'CD50'
#       Attached Changer: No
#
#     Note the "Product Type" value makes it clear which is which.
#
#     If it is not obvious, "mf -f /dev/... rewind" should be happy when
#     talking to a (loaded) tape drive but the changer should give some
#     kind of error.  Similarly, "mtx -f /dev/... status" should show good
#     results with the changer but fail with a tape drive device name.
#
#     Once you have this figured out, set "changerdev" in amanda.conf
#     to the changer device and "tapedev" to the tape device.
#
#   * Find out what the first and last storage slots are.  Running
#     "mtx -f /dev/... status" should give you something like this
#     (although the output will vary widely based on the version of mtx
#     and the specifics of your robot):
#
#	  Storage Changer /dev/changer:1 Drives, 9 Slots ( 0 Import/Export )
#	Data Transfer Element 0:Empty
#	      Storage Element 1:Full :VolumeTag=SR0001
#	      Storage Element 2:Full :VolumeTag=SR0002
#	      Storage Element 3:Full :VolumeTag=SR0003
#	      Storage Element 4:Full :VolumeTag=SR0004
#	      Storage Element 5:Full :VolumeTag=SR0005
#	      Storage Element 6:Full :VolumeTag=SR0006
#	      Storage Element 7:Full :VolumeTag=SR0007
#	      Storage Element 8:Full :VolumeTag=SR0008
#	      Storage Element 9:Full :VolumeTag=SR0009
#	      Storage Element 10 IMPORT/EXPORT:Full :VolumeTag=SR0009
#
#     This says the first storage slot (element) is "1" and the last
#     is "9".  If you allocate the entire robot to Amanda, you do not need
#     to set the "firstslot" or "lastslot" configuration file variables --
#     the script will compute these values for you.
#
#     You do not have to allocate all of the slots for Amanda use,
#     but whatever slots you use must be contiguous (i.e. 4 through 9
#     in the above would be OK but 1, 2, 5, 6, 9 would not).  The one
#     exception to this is that if one of the slots contains a cleaning
#     cartridge, it may be in any slot (Amanda will just skip over it if
#     it is between firstslot and lastslot).
#
#   * Speaking of cleaning cartridges, if you have a storage slot dedicated
#     to one, figure out what slot it is in.  That slot number will go in
#     the "cleanslot" variable.
#
#     Also, decide if you want the changer script to automatically run
#     the cleaning tape through the drive after every so many mounts,
#     and how many mounts you want to do between cleanings.  If you
#     want the script to do this, set the "autoclean" variable to 1 and
#     the "autocleancount" to the number of mounts between cleanings.
#     If you do not want to do automatic cleanings (including not having
#     a cleaning cartridge in the robot), set "autoclean" to 0.
#
#     Note that only a count of mounts is used to determine when it is
#     time to clean.  The script does not try to detect if the drive is
#     requesting cleaning, or how much the drive was used on a given
#     mount.
#
#   * If you tell Amanda about a cleaning cartridge, whether for automatic
#     operation or manual (amtape <config> clean), you must also tell
#     the script how long it takes to run the cleaning cycle.  It is
#     impossible for the script to determine when the cleaning operation
#     is done, so the "cleancycle" variable is the number of seconds
#     the longest cleaning operation takes (you'll just have to figure
#     this out by watching it a few times, or maybe finding it in a tape
#     drive hardware manual).  The script will sleep for this length of
#     time whenever the cleaning tape is referenced.  The default is 120
#     seconds (two minutes).
#
#   * Figure out the drive slot number.  By default, it is set to 0.
#     In the example above, the tape drive ("Data Transfer Element")
#     is in slot 0. If your drive slot is not 0, you
#     need to set the drive slot number with the "driveslot" variable.
#
#   * Figure out whether your robot has a barcode reader and whether
#     your version of mtx supports it.  If you see "VolumeTag" entries
#     in the "mtx -f /dev/xxx status" output you did above, you have
#     a reader and mtx can work with it, so you may set the "havereader"
#     variable to 1.  The default is 0 (do not use a reader).
#
#   * Pick any tape to load and then determine if the robot can put it
#     away directly or whether an "offline" must be done first.
#
#     With the tape still mounted and ready, try to put the tape away
#     with "mtx".  If you get some kind of error, which is the most
#     common response, try "mt -f /dev/... offline", wait for the drive
#     to unload and make sure the robot takes no action on its own to
#     store the tape.  Assuming it does not, try the "mtx" command again
#     to store the tape.
#
#     If you had to issue the "mt -f /dev/... offline" before you could
#     use "mtx" to store the tape, set the "offline_before_unload"
#     variable to 1.  If "mtx" unloaded the drive and put the tape away
#     all by itself, set it to 0.
#
#   * Some drives and robots require a small delay between unloading the
#     tape and instructing the robot to move it back to storage.
#     For instance, if you try to grab the tape too soon on an ATL robot
#     with DLT tape drives, it will rip the leader out of the drive and
#     require sincerely painful hardware maintenance.
#
#     If you need a little delay, set the "unloadpause" variable to
#     the number of seconds to wait before trying to take a tape from
#     a drive back to storage.  The default is 0.
#
#   * Some drives also require a short pause after loading, or the drive
#     will return an I/O error during a test to see if it's online (which
#     this script uses "mt rewind" to test).  My drives don't recover from
#     this, and must be reloaded before they will come online after failing
#     such a test.  For this reason there is an "initial_poll_delay"
#     variable which will pause for a certain number of seconds before
#     looping through the online test for the first time.  The default is 0.
####

####
# Now you are ready to set up the variables in the changer configuration
# file.
#
# All variables are in "changerfile".conf where "changerfile" is set
# in amanda.conf.  For example, if amanda.conf has:
#
#	changerfile="/etc/amanda/Dailyset1/CHANGER"
#    or changerfile="/etc/amanda/Dailyset1/CHANGER.conf"
#
# the variables must be in "/etc/amanda/Dailyset1/CHANGER.conf".
# The ".conf" is appended only if it's not there".
#
# If "changerfile" is a relative path, it is relative to the directory
# that contains amanda.conf.  That also happens to be the directory Amanda
# makes current before running this script.
#
# Here is a commented out example file with all the variables and showing
# their default value (if any):
####
# firstslot=?		    #### First storage slot (element) -- required
# lastslot=?		    #### Last storage slot (element) -- required
# cleanslot=-1		    #### Slot with cleaner tape -- default is "-1"
#			    #### Set negative to indicate no cleaner available
# driveslot=0		    #### Drive slot number.  Defaults to 0
#			    #### Use the 'Data Transfer Element' you want
#
#   # Do you want to clean the drive after a certain number of accesses?
#   # NOTE - This is unreliable, since 'accesses' aren't 'uses', and we
#   #        have no reliable way to count this.  A single amcheck could
#   #        generate as many accesses as slots you have, plus 1.
#   # ALSO NOTE - many modern tape loaders handle this automatically.
#
# autoclean=0		    #### Set to '1' or greater to enable
#
# autocleancount=99	    #### Number of access before a clean.
#
# havereader=0		    #### If you have a barcode reader, set to 1.
#
# offline_before_unload=0   #### Does your robot require an
#			    #### 'mt offline' before mtx unload?
#
# poll_drive_ready=NN	    #### Time (seconds) between tests to see if
#			    #### the tape drive has gone ready (default: 3).
#
# max_drive_wait=NN	    #### Maximum time (seconds) to wait for the
#			    #### tape drive to become ready (default: 120).
#
# initial_poll_delay=NN	    #### initial delay after load before polling for
#			    #### readiness
#
# slotinfofile=FILENAME	    #### record slot information to this file, in
#			    #### the line-based format "SLOT LABEL\n"
#
####

####
# Now it is time to test the setup.  Do all of the following in the
# directory that contains the amanda.conf file, and do all of it as
# the Amanda user.
#
#   * Run this:
#
#       .../chg-zd-mtx -info
#       echo $?             #### (or "echo $status" if you use csh/tcsh)
#
#     You should get a single line from the script like this (the actual
#     numbers will vary):
#
#       5 9 1 1
#
#     The first number (5) is the "current" slot.  This may or may not be
#     the slot actually loaded at the moment (if any).  It is the slot
#     Amanda will try to use next.
#
#     The second number (9) is the number of slots.
#
#     The third number will always be "1" and indicates the changer is
#     capable of going backward.
#
#     The fourth number is optional.  If you set $havereader to 1, it
#     will be "1", otherwise it will not be present.
#
#     The exit code ($? or $status) should be zero.
#
#   * Run this:
#
#       .../chg-zd-mtx -reset
#       echo $?
#
#     The script should output a line like this:
#
#       1 /dev/rmt/0mn
#
#     The number at the first should match $firstslot.  The device name
#     after that should be your tape device.
#
#     The exit code ($? or $status) should be zero.
#
#   * Run this:
#
#       .../chg-zd-mtx -slot next
#       echo $?
#
#     The script should output a line like this:
#
#       2 /dev/rmt/0mn
#
#     The number at the first should be one higher than $firstslot.
#     The device name after that should be your tape device.
#
#     The exit code ($? or $status) should be zero.
#
#   * Run this:
#
#       .../chg-zd-mtx -slot current
#       echo $?
#
#     Assuming the tape is still loaded from the previous test, the
#     robot should not move and the script should report the same thing
#     the previous command did.
#
#   * If you continue to run "-slot next" commands, the robot should load
#     each tape in turn then wrap back around to the first when it
#     reaches $lasttape.  If $cleanslot is within the $firstslot to
#     $lastslot range, the script will skip over that entry.
#
#   * Finally, try some of the amtape commands and make sure they work:
#
#       amtape <config> reset
#       amtape <config> slot next
#       amtape <config> slot current
#
#   * If you set $havereader non-zero, now would be a good time to create
#     the initial barcode database:
#
#       amtape <config> update
####

################################################################################
# To debug this script, first look in /var/log/amanda.  The script
# uses one of two log files there, depending on what version of Amanda
# is calling it.  It may be chg-zd-mtx.YYYYMMDD*.debug, or it may be
# changer.debug.driveN where 'N' is the drive number.
#
# If the log file does not help, try running the script, **as the Amanda
# user**, in the amanda.conf directory with whatever set of args the log
# said were used when you had a problem.  If nothing else useful shows up
# in the output, try running the script with the DEBUG environment variable
# set non-null, e.g.:
#
#	env DEBUG=yes .../chg-zd-mtx ...
################################################################################

# source utility functions and values from configure
prefix=/usr
exec_prefix=${prefix}
amlibexecdir=/usr/lib/amanda
. ${amlibexecdir}/chg-lib.sh

test -n "$DEBUG" && set -x
TMPDIR="/tmp/amanda"
DBGDIR="/var/log/amanda"

argv0=$0
myname=`expr "$argv0" : '.*/\(.*\)'`

config=`pwd 2>/dev/null`
config=`expr "$config" : '.*/\(.*\)'`

###
# Functions to write a new log file entry and append more log information.
###

ds=`date '+%H:%M:%S' 2>/dev/null`
if [ $? -eq 0  -a  -n "$ds" ]; then
	logprefix=`echo "$ds" | sed 's/./ /g'`
else
	logprefix=""
fi

LogAppend() {
	if [ -z "$logprefix" ]; then
		echo "$@" >> $DBGFILE
	else
		echo "$logprefix" "$@" >> $DBGFILE
	fi
}

Log() {
	if [ -z "$logprefix" ]; then
		echo "===" "`date`" "===" >> $DBGFILE
		echo "$@" >> $DBGFILE
	else
		ds=`date '+%H:%M:%S' 2>/dev/null`
		echo "$ds" "$@" >> $DBGFILE
	fi
}

###
# Common exit function.
#
#   $1 = exit code
#   $2 = slot result
#   $3 = additional information (error message, tape devive, etc)
###

internal_call=0
Exit() {
	if [ $internal_call -gt 0 ]; then
		call_type=Return
	else
		call_type=Exit
	fi
	code=$1
	shift
	exit_slot=$1
	shift
	exit_answer="$@"
	Log $call_type "($code) -> $exit_slot $@"
	echo "$exit_slot" "$@"
	if [ $call_type = Return ]; then
		return $code
	fi
	amgetconf dbclose.$myname:$DBGFILE > /dev/null 2>&1
	exit $code
}

###
# Function to run another command and log it.
###

Run() {
	Log `_ 'Running: %s' "$*"`
	rm -f $stdout $stderr
	"$@" > $stdout 2> $stderr
	exitcode=$?
	Log `_ 'Exit code: %s' "$exitcode"`
	if [ -s $stdout ]
	then
		LogAppend Stdout:
		cat $stdout >> $DBGFILE
	fi
	if [ -s $stderr ]
	then
		LogAppend Stderr:
		cat $stderr >> $DBGFILE
	fi
	cat $stdout
	cat $stderr 1>&2
	return $exitcode
}

###
# Return success if the arg is numeric.
###

IsNumeric() {
	test -z "$1" && return 1
	x="`expr -- "$1" : "\([-0-9][0-9]*\)" 2>/dev/null`"
	return `expr X"$1" != X"$x"`
}

###
# Run $MTX status unless the previous output is still valid.
###

mtx_status_valid=0
get_mtx_status() {
	test -n "$DEBUG" && set -x
	if [ $mtx_status_valid -ne 0 ]; then
		return 0
	fi
	rm -f $mtx_status
	Run $MTX status > $mtx_status 2>&1
	status=$?
	if [ $status -eq 0 ]; then
		mtx_status_valid=1
	fi

	# shim this in here so that we get a completely new slotinfofile
	# every time we run mtx status
	regenerate_slotinfo_from_mtx

	return $status
}

###
# Determine the slot currently loaded.  Set $loadedslot to the slot
# currently loaded, or "-1", and $loadedbarcode to the corresponding
# barcode (or nothing).
###

get_loaded_info() {
	test -n "$DEBUG" && set -x
	get_mtx_status
	if [ $mtx_status_valid -eq 0 ]; then
		Exit 2 \
		     `_ '<none>'` \
		     `head -1 $mtx_status`
		return $?
	fi

	set x `sed -n '
/^Data Transfer Element:Empty/                          {
    s/.*/-1/p
    q
}
/^Data Transfer Element '$driveslot':Empty/             {
    s/.*/-1/p
    q
}
/^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/               {
    s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/\1 \2/p
    q
}
/^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/  {
    s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/\1 \2/p
    q
}
/^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded):VolumeTag *= *\([^     ]*\)/	{
    s/.*:VolumeTag *= *\([^     ]*\)/-2 \1/p
    q
}
/^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/                           {
    s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
    q
}
/^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/              {
    s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
    q
}
/^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/	{
    s/.*/-2/p
    q
}
' < $mtx_status 2>&1`
	shift					# get rid of the "x"
	loadedslot=$1
	loadedbarcode=$2
	if [ -z "$loadedslot" ]; then
		Exit 2 \
		    `_ '<none>'` \
		    "could not determine current slot, are you sure your drive slot is $driveslot"
		return $?			# in case we are internal
	fi

	#Use the current slot if it's empty and we don't know which slot is loaded'
	if [ $loadedslot -eq -2 ]; then
		set x `sed -n '
{
    /^.*Storage Element '$currentslot':Empty/ {
	s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
        q
    }
    /^.*Storage Element '$currentslot':Full/ {
	s/.*Storage Element \([0-9][0-9]*\):Full/-2/p
        q
    }
    /^.*Storage Element '$currentslot' IMPORT\/EXPORT:Empty/ {
	s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Empty/\1/p
        q
    }
    /^.*Storage Element '$currentslot' IMPORT\/EXPORT:Full/ {
	s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Full/-2/p
        q
    }
}
' < $mtx_status 2>& 1`
		shift				# get rid of the "x"
		loadedslotx=$1
		if [ ! -z $loadedslotx ]; then
			loadedslot=$loadedslotx
		fi
	fi

	#Use the first empty slot if we don't know which slot is loaded'
	if [ $loadedslot -eq -2 ]; then
		set x `sed -n '
{
    /^.*Storage Element \([0-9][0-9]*\):Empty/ {
	s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
        q
    }
    /^.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Empty/ {
	s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Empty/\1/p
        q
    }
}
' < $mtx_status 2>& 1`
		shift				# get rid of the "x"
		loadedslot=$1
	fi

	if IsNumeric "$loadedslot" ; then
		:
	else
		Exit 2 \
		     `_ '<none>'` \
		     "currently loaded slot ($loadedslot) not numeric"
		return $?			# in case we are internal
	fi
	Log       `_ 'STATUS   -> currently loaded slot = %s' "$loadedslot"`
	LogAppend `_ '         -> currently loaded barcode = "%s"' "$loadedbarcode"`
}

###
# Get a list of slots between $firstslot and $lastslot, if they are set.
# If they are not set, set them to the first and last slot seen on the
# assumption the entire robot is to be used (???).
###

slot_list=
get_slot_list() {
	test -n "$DEBUG" && set -x
	if [ -n "$slot_list" ]; then
		return
	fi
	get_mtx_status
	if [ $mtx_status_valid -eq 0 ]; then
		Exit 2 \
		     `_ '<none>'` \
		     `head -1 $mtx_status`
		return $?
	fi
	slot_list=`sed -n '
/^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
    s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
}
/^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
    s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
}
/^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ {
    : loop
    n
    /^.*Storage Element \([0-9][0-9]*\):Full/ {
        s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
        b loop
    }
    /^.*Storage Element \([0-9][0-9]*\):Empty/ {
	s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
    }
}
/^.*Storage Element \([0-9][0-9]*\):Full/ {
    s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
}
/^.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Full/ {
    s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Full.*/\1/p
}
' < $mtx_status 2>&1 | grep -v "^${cleanslot}\$" | sort -n`
	slot_list=`echo $slot_list`		# remove the newlines
	if [ $firstslot -lt 0 -o $lastslot -lt 0 ]; then
		last=$lastslot
		for slot in $slot_list; do
			if [ $firstslot -lt 0 ]; then
				Log `_ 'SLOTLIST -> firstslot set to %s' "$slot"`
				firstslot=$slot
			fi
			if [ $lastslot -lt 0 ]; then
				last=$slot
			fi
		done
		if [ $lastslot -lt 0 -a $last -ge 0 ]; then
			Log `_ 'SLOTLIST -> lastslot set to %s' "$last"`
			lastslot=$last
		fi
		if [ $firstslot -lt 0 ]; then
			Exit 2 \
			     `_ '<none>'` \
			     `_ 'cannot determine first slot'`
			return $?		# in case we are internal
		elif [ $lastslot -lt 0 ]; then
			Exit 2 \
			     `_ '<none>'` \
			     `_ 'cannot determine last slot'`
			return $?		# in case we are internal
		fi
	fi
	amanda_slot_list=
	for slot in $slot_list; do
		if [ $slot -ge $firstslot -a $slot -le $lastslot ]; then
			amanda_slot_list="$amanda_slot_list $slot"
		fi
	done
	if [ -z "$amanda_slot_list" ]; then
		Exit 2 \
		     `_ '<none>'` \
		     "no slots available"
		return $?			# in case we are internal
	fi
	slot_list="$amanda_slot_list"
}

###
# Read the labelfile and scan for a particular entry.
###

read_labelfile() {
	labelfile_entry_found=0
	labelfile_label=
	labelfile_barcode=

	lbl_search=$1
	bc_search=$2

	line=0
	while read lbl bc junk; do
		line=`expr $line + 1`
		if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then
			Log       `_ 'ERROR    -> Line %s malformed: %s %s %s' "$line" "$lbl" "$bc" "$junk"`
			LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
			Exit 2 \
			     `_ '<none>'` \
			     `_ 'Line %s malformed in %s: %s %s %s' "$line" "$labelfile" "$lbl" "$bc" "$junk"`
			return $?		# in case we are internal
		fi
		if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then
			if [ $labelfile_entry_found -ne 0 ]; then
				Log       `_ 'ERROR    -> Duplicate entries: %s line %s' "$labelfile" "$line"`
				LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
				Exit 2 \
				     `_ '<none>'` \
				     `_ 'Duplicate entries: %s line %s' "$labelfile" "$line"`
				return $?	# in case we are internal
			fi
			labelfile_entry_found=1
			labelfile_label=$lbl
			labelfile_barcode=$bc
		fi
	done
}

lookup_label_by_barcode() {
    [ -z "$1" ] && return
    read_labelfile "" "$1" < $labelfile
    echo "$labelfile_label"
}

lookup_barcode_by_label() {
    [ -z "$1" ] && return
    read_labelfile "$1" "" < $labelfile
    echo "$labelfile_barcode"
}

remove_from_labelfile() {
	labelfile=$1
	lbl_search=$2
	bc_search=$3

	internal_remove_from_labelfile "$lbl_search" "$bc_search" < $labelfile >$labelfile.new
	if [ $labelfile_entry_found -ne 0 ]; then
		mv -f $labelfile.new $labelfile
		LogAppend `_ 'Removed Entry "%s %s" from barcode database' "$labelfile_label" "$labelfile_barcode"`
	fi
}

internal_remove_from_labelfile() {
	labelfile_entry_found=0
	labelfile_label=
	labelfile_barcode=

	lbl_search=$1
	bc_search=$2

	line=0
	while read lbl bc junk; do
		line=`expr $line + 1`
		if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then
			Log       `_ 'ERROR    -> Line %s malformed: %s %s %s' "$line" "$lbl" "$bc" "$junk"`
			LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
			Exit 2 \
			     `_ '<none>'` \
			     `_ 'Line %s malformed in %s: %s %s %s' "$line" "$labelfile" "$lbl" "$bc" "$junk"`
			return $?		# in case we are internal
		fi
		if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then
			if [ $labelfile_entry_found -ne 0 ]; then
				Log       `_ 'ERROR    -> Duplicate entries: %s line %s' "$labelfile" "$line"`
				LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
				Exit 2 \
				     `_ '<none>'` \
				     `_ 'Duplicate entries: %s line %s' "$labelfile" "$line"`
				return $?	# in case we are internal
			fi
			labelfile_entry_found=1
			labelfile_label=$lbl
			labelfile_barcode=$bc
		else
			echo $lbl $bc
		fi
	done
}

###
# Add a new slot -> label correspondance to the slotinfo file, removing any previous
# information about that slot.
###

record_label_in_slot() {
    [ -z "$slotinfofile" ] && return
    newlabel="$1"
    newslot="$2"

    (
	if [ -f "$slotinfofile" ]; then
		grep -v "^$newslot " < "$slotinfofile"
	fi
	echo "$newslot $newlabel"
    ) > "$slotinfofile~"
    mv "$slotinfofile~" "$slotinfofile"
}

###
# Remove a slot from the slotinfo file
###

remove_slot_from_slotinfo() {
    [ -z "$slotinfofile" ] && return
    emptyslot="$1"

    (
	if [ -f "$slotinfofile" ]; then
		grep -v "^$emptyslot " < "$slotinfofile"
	fi
    ) > "$slotinfofile~"
    mv "$slotinfofile~" "$slotinfofile"
}

###
# Assuming get_mtx_status has been run,
# - if we have barcodes, regenerate the slotinfo file completely by
#   mapping barcodes in the status into labels using the labelfile
# - otherwise, remove all empty slots from the slotinfo file
###

regenerate_slotinfo_from_mtx() {
    [ -z "$slotinfofile" ] && return
    [ "$mtx_status_valid" = "1" ] || return

    if [ "$havereader" = "1" ]; then
	# rewrite slotinfo entirely based on the status, since it has barcodes
	:> "$slotinfofile~"
	sed -n '/.*Storage Element \([0-9][0-9]*\).*VolumeTag *= *\([^ ]*\) *$/{
s/.*Storage Element \([0-9][0-9]*\).*VolumeTag *= *\([^ ]*\) *$/\1 \2/
p
}' < $mtx_status | while read newslot newbarcode; do
		newlabel=`lookup_label_by_barcode "$newbarcode"`
		if [ -n "$newlabel" ]; then
		    echo "$newslot $newlabel" >> "$slotinfofile~"
		fi
	    done
	mv "$slotinfofile~" "$slotinfofile"
    else
	# just remove empty slots from slotinfo

	# first determine which slots are not really empty, but are
	# loaded into a data transfer element
loadedslots=`sed -n '/.*(Storage Element \([0-9][0-9]*\) Loaded).*/{
s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/g
p
}' < $mtx_status`

	# now look for any slots which are empty, but which aren't
	# in the set of loaded slots
	sed -n '/.*Storage Element \([0-9][0-9]*\): *Empty.*/{
s/.*Storage Element \([0-9][0-9]*\): *Empty.*/\1/g
p
}' < $mtx_status | while read emptyslot; do
	    reallyempty=1
	    if [ -n "$loadedslots" ]; then
		for loadedslot in $loadedslots; do
		    [ "$loadedslot" = "$emptyslot" ] && reallyempty=0
		done
	    fi
	    if [ "$reallyempty" = "1" ]; then
		remove_slot_from_slotinfo "$emptyslot"
	    fi
	done
    fi
}

DBGFILE=`amgetconf dbopen.$myname 2>/dev/null`
if [ -z "$DBGFILE" ]
then
	DBGFILE=/dev/null			# will try this again below
fi

changerfile=`amgetconf changerfile 2>/dev/null`
if [ -z "$changerfile" ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "changerfile must be specified in amanda.conf"
fi

rawtape=`amgetconf tapedev 2>/dev/null`
if [ -z "$rawtape" ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "tapedev may not be empty"
fi
tape=`tape_device_filename "$rawtape"`
if [ -z "$tape" ]; then
        Exit 2 \
             ` _ '<none>'` \
             "tapedev $rawtape is not a tape device."
elif [ $tape = "/dev/null" -o `expr "$tape" : 'null:'` -eq 5 ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "tapedev ($tape) may not be the null device"
fi
# Confusingly, TAPE is the name of the changer device...
TAPE=`amgetconf changerdev 2>/dev/null`
if [ -z "$TAPE" ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "changerdev may not be empty"
elif [ $TAPE = "/dev/null" ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "changerdev ($TAPE) may not be the null device"
fi
export TAPE					# for mtx command

CHANGER=$TAPE 
export CHANGER					# for mtx command

#### Set up the various config files.

conf_match=`expr "$changerfile" : .\*\.conf\$`
if [ $conf_match -ge 6 ]; then
	configfile=$changerfile
	changerfile=`echo $changerfile | sed 's/.conf$//g'`
else
	configfile=$changerfile.conf
fi

if [ ! -e $configfile ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "configuration file \"$configfile\" doesn't exist"
fi
if [ ! -f $configfile ]; then
	Exit 2 \
	     `_ '<none>'` \
	     "configuration file \"$configfile\" is not a file"
fi

cleanfile=$changerfile-clean
accessfile=$changerfile-access
slotfile=$changerfile-slot
labelfile=$changerfile-barcodes
slotinfofile=""
[ ! -s $cleanfile ] && echo 0 > $cleanfile
[ ! -s $accessfile ] && echo 0 > $accessfile
[ ! -s $slotfile ] && echo -1 > $slotfile
[ ! -f $labelfile ] && > $labelfile
cleancount=`cat $cleanfile`
accesscount=`cat $accessfile`

#### Dig out of the config file what is needed

varlist=
varlist="$varlist firstslot"
varlist="$varlist lastslot"
varlist="$varlist cleanslot"
varlist="$varlist cleancycle"
varlist="$varlist OFFLINE_BEFORE_UNLOAD"	# old name
varlist="$varlist offline_before_unload"
varlist="$varlist unloadpause"
varlist="$varlist AUTOCLEAN"			# old name
varlist="$varlist autoclean"
varlist="$varlist autocleancount"
varlist="$varlist havereader"
varlist="$varlist driveslot"
varlist="$varlist poll_drive_ready"
varlist="$varlist initial_poll_delay"
varlist="$varlist max_drive_wait"
varlist="$varlist slotinfofile"

for var in $varlist
do
	val="`cat $configfile 2>/dev/null | sed -n '
# Ignore comment lines (anything starting with a #).
/^[ 	]*#/d
# Find the first var=val line in the file, print the value and quit.
/^[ 	]*'$var'[ 	]*=[ 	]*\([^ 	][^ 	]*\).*/	{
	s/^[ 	]*'$var'[ 	]*=[ 	]*\([^ 	][^ 	]*\).*/\1/p
	q
}
'`"
	eval $var=\"$val\"
done

# Deal with driveslot first so we can get DBGFILE set if we are still
# using the old amgetconf.

if [ -z "$driveslot" ]; then
	driveslot=0;
fi

# Get DBGFILE set if it is not already.

if [ $DBGFILE = /dev/null ]; then
	if [ -d "$DBGDIR" ]; then
		DBGFILE=$DBGDIR/changer.debug.drive$driveslot
	else
		DBGFILE=/dev/null
	fi
	Log `_ '=== Start %s ===' "\`date\`"`
fi

stdout=$TMPDIR/$myname.1.$$
stderr=$TMPDIR/$myname.2.$$
mtx_status=$TMPDIR/$myname.status.$$
trap "rm -f $stdout $stderr $mtx_status" 0	# exit cleanup

Log `_ 'Using config file %s' "$configfile"`

# Log the argument list.

Log `_ "Arg info:"`
LogAppend "\$# = $#"
i=0
LogAppend "\$$i = \"$argv0\""
for arg in "$@"; do
	i=`expr $i + 1`
	LogAppend "\$$i = \"$arg\""
done

# Set the default config values for those not in the file.  Log the
# results and make sure each is valid (numeric).

firstslot=${firstslot:-'-1'}				# default: mtx status
lastslot=${lastslot:-'-1'}				# default: mtx status
cleanslot=${cleanslot:-'-1'}				# default: -1
cleancycle=${cleancycle:-'120'}				# default: two minutes
if [ -z "$offline_before_unload" -a -n "$OFFLINE_BEFORE_UNLOAD" ]; then
	offline_before_unload=$OFFLINE_BEFORE_UNLOAD	# (old name)
fi
offline_before_unload=${offline_before_unload:-'0'}	# default: 0
unloadpause=${unloadpause:-'0'}				# default: 0
if [ -z "$autoclean" -a -n "$AUTOCLEAN" ]; then
	autoclean=$AUTOCLEAN				# (old name)
fi
autoclean=${autoclean:-'0'}				# default: 0
autocleancount=${autocleancount:-'99'}			# default: 99
havereader=${havereader:-'0'}				# default: 0
poll_drive_ready=${poll_drive_ready:-'3'}		# default: three seconds
initial_poll_delay=${initial_poll_delay:-'0'}		# default: zero zeconds
max_drive_wait=${max_drive_wait:-'120'}			# default: two minutes

# check MT and MTX for sanity
if test "${MTX%${MTX#?}}" = "/"; then
    if ! test -f "${MTX}"; then
	Exit 2 \
	    `_ '<none>'` \
	    `_ "mtx binary at '%s' not found" "$MTX"`
    fi
    if ! test -x "${MTX}"; then
	Exit 2 \
	    `_ '<none>'` \
	    `_ "mtx binary at '%s' is not executable" "$MTX"`
    fi
else
    # try running it to see if the shell can find it
    "$MTX" >/dev/null 2>/dev/null
    if test $? -eq 127 -o $? -eq 126; then
	Exit 2 \
	    `_ '<none>'` \
	    `_ "Could not run mtx binary at '%s'" "$MTX"`
    fi
fi

error=`try_find_mt`
if test $? -ne 0; then
    Exit 2 '<none>' $error
fi

get_slot_list

Log `_ "Config info:"`
for var in $varlist; do
	if [ $var = "OFFLINE_BEFORE_UNLOAD" ]; then
		continue			# old name
	elif [ $var = "AUTOCLEAN" ]; then
		continue			# old name
	elif [ $var = "slotinfofile" ]; then
		continue			# not numeric
	fi
	eval val=\"'$'$var\"
	if [ -z "$val" ]; then
		Exit 2 \
		     `_ '<none>'` \
		     `_ '%s missing in %s' "$var" "$configfile"`
	fi
	if IsNumeric "$val" ; then
		:
	else
		Exit 2 \
		     `_ '<none>'` \
		     `_ '%s (%s) not numeric in %s' "$var" "$val" "$configfile"`
	fi
	LogAppend $var = \"$val\"
done

# Run the rest of the config file sanity checks.

if [ $firstslot -gt $lastslot ]; then
	Exit 2 \
	     `_ '<none>'` \
	     `_ 'firstslot (%s) greater than lastslot (%s) in %s' "$firstslot" "$lastslot" "$configfile"`
fi
if [ $autoclean -ne 0 -a $cleanslot -lt 0 ]; then
	Exit 2 \
	     `_ '<none>'` \
	     `_ 'autoclean set but cleanslot not valid (%s)' "$cleanslot"`
fi

# Set up the current slot

currentslot=`cat $slotfile`
if IsNumeric "$currentslot" ; then
	if [ $currentslot -lt $firstslot ]; then
		Log `_ 'SETUP    -> current slot %s less than %s ... resetting to %s' "$currentslot" "$firstslot" "$firstslot"`
		currentslot=$firstslot
	elif [ $currentslot -gt $lastslot ]; then
		Log `_ 'SETUP    -> current slot %s greater than %s ... resetting to %s' "$currentslot" "$lastslot" "$lastslot"`
		currentslot=$lastslot
	fi
else
	Log `_ 'SETUP    -> contents of %s (%s) invalid, setting current slot to first slot (%s)' "$slotfile" "$currentslot" "$firstslot"`
	currentslot=$firstslot
fi

found_current=0
first_slot_in_list=-1
next_slot_after_current=-1
for slot in $slot_list; do
	if [ $first_slot_in_list -lt 0 ]; then
		first_slot_in_list=$slot	# in case $firstslot is missing
	fi
	if [ $slot -eq $currentslot ]; then
		found_current=1
		break
	elif [ $slot -gt $currentslot ]; then
		next_slot_after_current=$slot	# $currentslot is missing
		break
	fi
done
if [ $found_current -eq 0 ]; then
	if [ $next_slot_after_current -lt 0 ]; then
		new_currentslot=$first_slot_in_list
	else
		new_currentslot=$next_slot_after_current
	fi
	Log `_ 'WARNING  -> current slot %s not available, setting current slot to next slot (%s)' "$currentslot" "$new_currentslot"`
	currentslot=$new_currentslot
fi

# More routines.

###
# Eject the current tape and put it away.
###

eject() {
	test -n "$DEBUG" && set -x
	Log `_ 'EJECT    -> ejecting tape from %s' "$tape"`
	get_loaded_info 
	if [ $loadedslot -gt 0 ]; then
		Log `_ 'EJECT    -> moving tape from drive %s to storage slot %s' "$driveslot" "$loadedslot"`
		if [ $offline_before_unload -ne 0 ]; then
                        Run try_eject_device $tape
		fi
		sleep $unloadpause
		result=`Run $MTX unload $loadedslot $driveslot 2>&1`
		status=$?
		Log `_ '         -> status %s, result "%s"' "$status" "$result"`
		mtx_status_valid=0
		if [ $status -ne 0 ]; then
			answer="$result"
			code=2
		else
			answer="$rawtape"
			code=0
		fi
	else
		answer=`_ 'Drive was not loaded'`
		code=1
	fi
	Exit $code "$loadedslot" "$answer"
	return $?				# in case we are internal
}

###
# Reset the robot back to the first slot.
###

reset() {
	test -n "$DEBUG" && set -x
	Log `_ 'RESET    -> loading tape from slot %s to drive %s (%s)' "$firstslot" "$driveslot" "$tape"`
	# Call loadslot without doing it as an internal and let it finish
	# things up.
	loadslot $firstslot
	# NOTREACHED
	Exit 2 `_ '<none>'` `_ 'reset: should not get here'`
	return $?				# in case we are internal
}

###
# Unload the current tape (if necessary) and load a new one (unless
# "advance").  If no tape is loaded, get the value of "current" from
# $slotfile.
###

loadslot() {
	test -n "$DEBUG" && set -x
	if [ $# -lt 1 ]; then
		Exit 2 `_ '<none>'` `_ 'Missing -slot argument'`
		return $?			# in case we are internal
	fi
	whichslot=$1
	Log `_ 'LOADSLOT -> load drive %s (%s) from slot %s' "$driveslot" "$tape" "$whichslot"`

	numeric=`echo $whichslot | sed 's/[^0-9]//g'`
	case $whichslot in
	current|prev|next|advance)
		find_slot=$currentslot
		;;
	first)
		find_slot=$firstslot
		;;
	last)
		find_slot=$lastslot
		;;
	$numeric)
		find_slot=$numeric
		;;
	clean)
		find_slot=$cleanslot
		;;
	*)
		Exit 2 `_ '<none>'` `_ 'Illegal slot: "%s"' "$whichslot"`
		return $?			# in case we are internal
		;;
	esac

	# Find the requested slot in the slot list.  By loading the "set"
	# command with multiple copies, we guarantee that if the slot is
	# found, we can look both forward and backward without running
	# off the end.	Putting $cleanslot at the end allows us to find
	# that slot since it is not in $slot_list.
	get_slot_list
	set x $slot_list $slot_list $slot_list $cleanslot
	shift					# get rid of the "x"
	prev_slot=$1
	shift
	while [ $# -gt 0 ]; do
		if [ $1 -eq $find_slot ]; then
			break
		fi
		prev_slot=$1
		shift
	done
	if [ $# -le 0 ]; then
		if [ $find_slot -ge $firstslot -a $find_slot -le $lastslot ]; then
			Exit 2 \
			     `_ '<none>'` \
			     `_ 'Cannot find a tape in slot %s' "$find_slot "`
			return $?			# in case we are internal
		else
			Exit 2 \
			     `_ '<none>'` \
			     `_ 'Cannot find slot %s in slot list (%s)' "$find_slot " "$slot_list"`
			return $?			# in case we are internal
		fi
	fi

	# Determine the slot to load.
	case $whichslot in
	next|advance)
		shift
		loadslot=$1
		;;
	prev)
		loadslot=$prev_slot
		;;
	*)
		loadslot=$find_slot
	esac

	# If the desired slot is already loaded, we are done.  Only update
	# current slot if this is not the cleaning slot.
	get_loaded_info
	if [ $loadslot = $loadedslot ]; then
		if [ $loadslot -ne $cleanslot ]; then
			rm -f $slotfile
			echo $loadslot > $slotfile
		fi
		Exit 0 "$loadedslot" "$rawtape"
		return $?			# in case we are internal
	fi
	if [ $loadedslot -eq -2 ]; then
		Exit 0 "$loadedslot" "$rawtape"
		return $?			# in case we are internal
        fi

	# If we are loading the cleaning tape, bump the cleaning count
	# and reset the access count.  Otherwise, bump the access count
	# and see if it is time to do a cleaning.
	if [ $loadslot = $cleanslot ]; then
		rm -f $cleanfile $accessfile
		expr $cleancount + 1 > $cleanfile
		echo 0 > $accessfile
	else
		rm -f $accessfile
		expr $accesscount + 1 > $accessfile
		if [ $autoclean -ne 0 -a $accesscount -gt $autocleancount ]
		then
			internal_call=`expr $internal_call + 1`
			loadslot clean > /dev/null 2>&1
			status=$?
			internal_call=`expr $internal_call - 1`
			if [ $status -ne 0 ]; then
				Exit $status "$loadslot" "$exit_answer"
				return $?	# in case we are internal
			fi

			# Slot $cleanslot might contain an ordinary tape
			# rather than a cleaning tape.  A cleaning tape
			# *MIGHT* auto-eject; an ordinary tape does not.
			# We therefore have to read the status again to
			# check what actually happened.
			mtx_status_valid=0
			get_loaded_info
		fi
	fi

	# Unload whatever tape is in the drive.
	internal_call=`expr $internal_call + 1`
	eject > /dev/null 2>&1
	status=$?
	internal_call=`expr $internal_call - 1`
	if [ $status -gt 1 ]; then
		Exit $status "$exit_slot" "$exit_answer"
		return $?			# in case we are internal
	fi

	# If we were doing an "advance", we are done.
	if [ $whichslot = advance ]; then
		if [ $loadslot -ne $cleanslot ]; then
			rm -f $slotfile
			echo $loadslot > $slotfile
		fi
		Exit 0 "$loadslot" "/dev/null"
		return $?			# in case we are internal
	fi

	# Load the tape, finally!
	Log `_ "LOADSLOT -> loading tape from slot %s to drive %s (%s)" "$loadslot" "$driveslot" "$tape"`
	result=`Run $MTX load $loadslot $driveslot 2>&1`
	status=$?
	Log `_ '         -> status %s, result "%s"' "$status" "$result"`
	mtx_status_valid=0
	if [ $status -ne 0 ]; then
		Exit 2 "$loadslot" "$result"
		return $?			# in case we are internal
	fi

	###
	# Cleaning tapes never go "ready", so instead we just sit here
	# for "long enough" (as determined empirically by the user),
	# then return success.
	###
	if [ $loadslot -eq $cleanslot ]; then
		Run sleep $cleancycle
		Exit 0 "$loadslot" "$rawtape"
		return $?			# in case we are internal
	fi

	###
	# Wait for the drive to go online.
	###
	waittime=0
	ready=0
	sleep $initial_poll_delay
	while [ $waittime -lt $max_drive_wait ]; do
                amdevcheck_status $tape
		if [ $? -eq 0 ]; then
			ready=1
			break
		fi
		sleep $poll_drive_ready
		waittime=`expr $waittime + $poll_drive_ready`
	done
	if [ $ready -eq 0 ]; then
		Exit 2 "$loadslot" `_ 'Drive not ready after %s seconds: %s' "$max_drive_wait" "$amdevcheck_message"`
		return $?			# in case we are internal
	fi

	if [ $loadslot -ne $cleanslot ]; then
		rm -f $slotfile
		echo $loadslot > $slotfile
	fi
	Exit 0 "$loadslot" "$rawtape"
	return $?				# in case we are internal
}

###
# Return information about how the changer is configured and the current
# state of the robot.
###

info() {
	test -n "$DEBUG" && set -x
	get_loaded_info
	get_slot_list
	Log       `_ 'INFO     -> first slot: %s' "$firstslot"`
	LogAppend `_ '         -> current slot: %s' "$currentslot"`
	LogAppend `_ '         -> loaded slot: %s' "$loadedslot"`
	LogAppend `_ '         -> last slot: %s' "$lastslot"`
	LogAppend `_ '         -> slot list: %s' "$slot_list"`
	LogAppend `_ '         -> can go backwards: 1'`
	LogAppend `_ '         -> havereader: %s' "$havereader"`

        ###
	# Check if a barcode reader is configured or not.  If so, it
	# passes the 4th item in the echo back to amtape signifying it
	# can search based on barcodes.
	###
	reader=
        if [ $havereader -eq 1 ]; then
		reader=1
        fi

	if [ $currentslot -lt $firstslot -o $currentslot -gt $lastslot ]; then
		currentslot=$firstslot		# what "current" will get
	fi
	numslots=`expr $lastslot - $firstslot + 1`
	Exit 0 "$currentslot" "$numslots 1 $reader"
	return $?				# in case we are internal
}

###
# Adds the label and barcode for the currently loaded tape to the
# barcode file.  Return an error if the database is messed up.
###

addlabel() {
	test -n "$DEBUG" && set -x
	if [ $# -lt 1 ]; then
		Exit 2 `_ '<none>'` `_ 'Missing -label argument'`
		return $?			# in case we are internal
	fi
        tapelabel=$1
        get_loaded_info
	if [ $loadedslot -lt 0 ]; then
		Exit 1 `_ '<none>'` `_ 'No tape currently loaded'`
		return $?			# in case we are internal
	fi
	record_label_in_slot "$tapelabel" "$loadedslot"
	if [ $havereader -eq 0 ]; then
		Exit 0 "$loadedslot" "$rawtape"	# that's all we needed
		return $?			# in case we are internal
	fi
	if [ -z "$loadedbarcode" ]; then
		Exit 1 `_ '<none>'` `_ 'No barcode found for tape %s.' $tapelabel`
		return $?			# in case we are internal
	fi
	Log       `_ 'LABEL    -> Adding label "%s" with barcode "%s" for slot %s into %s' "$tapelabel" "$loadedbarcode" "$loadedslot" "$labelfile"`
	read_labelfile "$tapelabel" "$loadedbarcode" < $labelfile
	if [ $labelfile_entry_found -ne 0 ]; then
		lf_val=
		if [ "$labelfile_barcode" != "$loadedbarcode" ]; then
			lf_type=label
			lf_val=$tapelabel
			val_type=barcode
			old_val=$labelfile_barcode
			new_val=$loadedbarcode
		elif [ "$labelfile_label" != "$tapelabel" ]; then
			lf_type=barcode
			lf_val=$loadedbarcode
			val_type=label
			old_val=$labelfile_label
			new_val=$tapelabel
		fi
		if [ -n "$lf_val" ]; then
			if [ "$val_type" = "barcode" ]; then
				remove_from_labelfile $labelfile "" "$old_val"
			else
				remove_from_labelfile $labelfile "$old_val" ""
			fi
			echo "$tapelabel $loadedbarcode" >> $labelfile
			LogAppend `_ '         -> appended %s entry: %s %s' "$labelfile" "$tapelabel" "$loadedbarcode"`
		else
			LogAppend `_ "         -> already synced"`
		fi
	else
		echo "$tapelabel $loadedbarcode" >> $labelfile
		LogAppend `_ '         -> appended %s entry: %s %s' "$labelfile" "$tapelabel" "$loadedbarcode"`
	fi
	Exit 0 "$loadedslot" "$rawtape"
	return $?				# in case we are internal
}

###
# Look for a label in the barcode file.  If found, locate the slot it's
# in by looking for the barcode in the mtx output, then load that tape.
###

searchtape() {
	test -n "$DEBUG" && set -x
	if [ $# -lt 1 ]; then
		Exit 2 `_ '<none>'` `_ 'Missing -search argument'`
		return $?			# in case we are internal
	fi
        tapelabel=$1
	if [ $havereader -eq 0 ]; then
		Exit 2 `_ '<none>'` `_ 'Not configured with barcode reader'`
		return $?			# in case we are internal
	fi
	Log `_ 'SEARCH   -> Hunting for label "%s"' "$tapelabel"`
	read_labelfile "$tapelabel" "" < $labelfile
	if [ $labelfile_entry_found -eq 0 ]; then
		LogAppend `_ '         -> !!! label "%s" not found in %s !!!' "$tapelabel" "$labelfile"`
		LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
		Exit 1 \
		     `_ '<none>'` \
		     `_ '%s: label "%s" not found in %s' "$tapelabel" "$tapelabel" "$labelfile"`
		return $?			# in case we are internal
	fi
	LogAppend `_ '         -> barcode is "%s"' "$labelfile_barcode"`
	get_mtx_status
	if [ $mtx_status_valid -eq 0 ]; then
		Exit 2 \
		     `_ '<none>'` \
		     `head -1 $mtx_status`
		return $?
	fi
	foundslot=`sed -n '
/VolumeTag *= *'$labelfile_barcode' *$/			{
	s/.*Storage Element \([0-9][0-9]*\).*/\1/p
	q
}
' < $mtx_status`
	LogAppend `_ '         -> foundslot is %s' "$foundslot"`
	if [ -z "$foundslot" ]; then
		LogAppend `_ 'ERROR    -> !!! Could not find slot for barcode "%s"!!!' "$labelfile_barcode"`
		LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
		Exit 1 \
		     `_ '<none>'` \
		     `_ 'barcode "%s" not found in mtx status output' "$labelfile_barcode"`
		return $?			# in case we are internal
	fi
	# Call loadslot without doing it as an internal and let it finish
	# things up.
	loadslot $foundslot
	# NOTREACHED
	Exit 2 `_ '<none>'` `_ 'searchtape: should not get here'`
	return $?				# in case we are internal
}

###
# Program invocation begins here
###

if [ $# -lt 1 ]; then
	Exit 2 `_ '<none>'` `_ 'Usage: %s -command args' "$myname"`
fi
cmd=$1
shift
case "$cmd" in
-slot)
	loadslot "$@"
	;;
-info)
	info "$@"
	;;
-reset)
	reset "$@"
	;;
-eject)
	eject "$@"
	;;
-label) 
	addlabel "$@"
	;;
-search)
	searchtape "$@"
	;;
-clean)
	loadslot clean
	;;
*)
	Exit 2 `_ '<none>'` `_ 'unknown option: %s' "$cmd"`
	;;
esac

Exit 2 `_ '<none>'` `_ '%s: should not get here' "$myname"`