]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dircmd.c
Make Recycle work -- kes25May02
[bacula/bacula] / bacula / src / stored / dircmd.c
1 /*
2  *  This file handles accepting Director Commands
3  *
4  *    Most Director commands are handled here, with the 
5  *    exception of the Job command command and subsequent 
6  *    subcommands that are handled
7  *    in job.c.  
8  *
9  *    File daemon commands are handled in fdcmd.c
10  *
11  *     Kern Sibbald, May MMI
12  *
13  *   Version $Id$
14  *  
15  */
16 /*
17    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
18
19    This program is free software; you can redistribute it and/or
20    modify it under the terms of the GNU General Public License as
21    published by the Free Software Foundation; either version 2 of
22    the License, or (at your option) any later version.
23
24    This program is distributed in the hope that it will be useful,
25    but WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27    General Public License for more details.
28
29    You should have received a copy of the GNU General Public
30    License along with this program; if not, write to the Free
31    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32    MA 02111-1307, USA.
33
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38
39 /* Exported variables */
40
41 /* Imported variables */
42 extern BSOCK *filed_chan;
43 extern int r_first, r_last;
44 extern struct s_res resources[];
45 extern char my_name[];
46 extern time_t daemon_start_time;
47 extern struct s_last_job last_job;
48
49 /* Static variables */
50 static char derrmsg[]       = "3900 Invalid command\n";
51 static char OKsetdebug[]   = "3000 OK setdebug=%d\n";
52
53
54 /* Imported functions */
55 extern void terminate_child();
56 extern int job_cmd(JCR *jcr);
57
58 /* Forward referenced functions */
59 static int label_cmd(JCR *jcr);
60 static int setdebug_cmd(JCR *jcr);
61 static int cancel_cmd(JCR *cjcr);
62 static int mount_cmd(JCR *jcr);
63 static int unmount_cmd(JCR *jcr);
64 static int status_cmd(JCR *sjcr);
65 static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *vname, char *poolname);
66
67 struct s_cmds {
68    char *cmd;
69    int (*func)(JCR *jcr);
70 };
71
72 /*  
73  * The following are the recognized commands from the Director. 
74  */
75 static struct s_cmds cmds[] = {
76    {"JobId=",    job_cmd},            /* start Job */
77    {"setdebug=", setdebug_cmd},       /* set debug level */
78    {"cancel",    cancel_cmd},
79    {"label",     label_cmd},          /* label a tape */
80    {"mount",     mount_cmd},
81    {"unmount",   unmount_cmd},
82    {"status",    status_cmd},
83    {NULL,        NULL}                /* list terminator */
84 };
85
86
87 /* 
88  * Connection request. We accept connections either from the 
89  *  Director or a Client.
90  * 
91  * Note, we are running as a seperate thread of the Storage daemon.
92  *  and it is because a Director has made a connection with
93  *  us on the "Message" channel.    
94  *
95  * Basic tasks done here:  
96  *  - Create a JCR record
97  *  - Authenticate the Director
98  *  - We wait for a command
99  *  - We execute the command
100  *  - We continue or exit depending on the return status
101  */
102 void connection_request(void *arg)
103 {
104    BSOCK *bs = (BSOCK *)arg;
105    JCR *jcr;
106    int i, found, quit;
107    int bnet_stat;
108    char name[MAX_NAME_LENGTH];
109
110    if (bnet_recv(bs) <= 0) {
111       Emsg0(M_ERROR, 0, "Connection request failed.\n");
112       return;
113    }
114
115    /* 
116     * See if this is a File daemon connection
117     */
118    if (sscanf(bs->msg, "Hello Start Job %127s calling\n", name) == 1) {
119       handle_filed_connection(bs, name);
120       return;
121    }
122    
123    jcr = new_jcr(sizeof(JCR), stored_free_jcr);     /* create Job Control Record */
124    jcr->dir_bsock = bs;               /* save Director bsock */
125
126    Dmsg0(1000, "stored in start_job\n");
127
128    /*
129     * Authenticate the Director
130     */
131    if (!authenticate_director(jcr)) {
132       Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate Director\n"));
133       free_jcr(jcr);
134       return;
135    }
136    Dmsg0(90, "Message channel init completed.\n");
137
138    for (quit=0; !quit;) {
139
140       /* Read command */
141       if ((bnet_stat = bnet_recv(bs)) <= 0) {
142          break;                       /* connection terminated */
143       }
144       Dmsg1(9, "<dird: %s\n", bs->msg);
145       found = FALSE;
146       for (i=0; cmds[i].cmd; i++) {
147          if (strncmp(cmds[i].cmd, bs->msg, strlen(cmds[i].cmd)) == 0) {
148             if (!cmds[i].func(jcr)) {    /* do command */
149                quit = TRUE;              /* error, get out */
150                Dmsg1(90, "Command %s requsts quit\n", cmds[i].cmd);
151             }
152             found = TRUE;            /* indicate command found */
153             break;
154          }
155       }
156       if (!found) {                   /* command not found */
157          bnet_fsend(bs, derrmsg);
158          quit = TRUE;
159          break;
160       }
161    }
162    if (bnet_stat != BNET_TERMINATE) {
163       bnet_sig(bs, BNET_TERMINATE);
164    }
165    free_jcr(jcr);
166    return;
167 }
168
169 /*
170  * Set debug level as requested by the Director
171  *
172  */
173 static int setdebug_cmd(JCR *jcr)
174 {
175    BSOCK *dir = jcr->dir_bsock;
176    int level;
177
178    Dmsg1(10, "setdebug_cmd: %s", dir->msg);
179    if (sscanf(dir->msg, "setdebug=%d", &level) != 1 || level < 0) {
180       bnet_fsend(dir, "3991 Bad setdebug command: %s\n", dir->msg);
181       return 0;
182    }
183    debug_level = level;
184    return bnet_fsend(dir, OKsetdebug, level);
185 }
186
187
188 /*
189  * Cancel a Job
190  */
191 static int cancel_cmd(JCR *cjcr)
192 {
193    BSOCK *dir = cjcr->dir_bsock;
194    int oldStatus;
195    char Job[MAX_NAME_LENGTH];
196    JCR *jcr;
197
198    if (sscanf(dir->msg, "cancel Job=%127s", Job) == 1) {
199       if (!(jcr=get_jcr_by_full_name(Job))) {
200          bnet_fsend(dir, _("3992 Job %s not found.\n"), Job);
201       } else {
202          P(jcr->mutex);
203          oldStatus = jcr->JobStatus;
204          jcr->JobStatus = JS_Cancelled;
205          if (!jcr->authenticated && jcr->JobStatus == JS_WaitFD) {
206             pthread_cond_signal(&jcr->job_start_wait); /* wake waiting thread */
207          }
208          V(jcr->mutex);
209          if (jcr->file_bsock) {
210             bnet_sig(jcr->file_bsock, BNET_TERMINATE);
211          }
212          bnet_fsend(dir, _("3000 Job %s Status=%c marked to be cancelled.\n"), 
213             jcr->Job, oldStatus);
214          free_jcr(jcr);
215       }
216    } else {
217       bnet_fsend(dir, _("3993 Error scanning cancel command.\n"));
218    }
219    bnet_sig(dir, BNET_EOF);
220    return 1;
221 }
222
223 /*
224  * Label a tape
225  *
226  */
227 static int label_cmd(JCR *jcr) 
228 {
229    char *dname, *volname, *poolname, *mtype;
230    BSOCK *dir = jcr->dir_bsock;
231    DEVRES *device;
232    DEVICE *dev;
233    int found = 0;
234
235    dname = (char *) get_memory(dir->msglen+1);
236    volname = (char *) get_memory(dir->msglen+1);
237    poolname = (char *) get_memory(dir->msglen+1);
238    mtype = (char *) get_memory(dir->msglen+1);
239    if (sscanf(dir->msg, "label %s VolumeName=%s PoolName=%s MediaType=%s", 
240        dname, volname, poolname, mtype) == 4) {
241       unbash_spaces(dname);
242       unbash_spaces(volname);
243       unbash_spaces(poolname);
244       unbash_spaces(mtype);
245       device = NULL;
246       LockRes();
247       while ((device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device))) {
248          /* Find resource, and make sure we were able to open it */
249          if (strcmp(device->hdr.name, dname) == 0 && device->dev) {
250             Dmsg1(20, "Found device %s\n", device->hdr.name);
251             found = 1;
252             break;
253          }
254       }
255       UnlockRes();
256       if (found) {
257          /******FIXME**** compare MediaTypes */
258          jcr->device = device;
259          dev = device->dev;
260          P(dev->mutex);
261          if (!(dev->state & ST_OPENED)) {
262             if (open_dev(dev, volname, READ_WRITE) < 0) {
263                bnet_fsend(dir, _("3994 Connot open device: %s\n"), strerror_dev(dev));
264             } else {
265                label_volume_if_ok(jcr, dev, volname, poolname);
266                force_close_dev(dev);
267             }
268          } else if (dev->dev_blocked && 
269                     dev->dev_blocked != BST_DOING_ACQUIRE) {  /* device blocked? */
270             label_volume_if_ok(jcr, dev, volname, poolname);
271          } else if (dev->state & ST_READ || dev->num_writers) {
272             if (dev->state & ST_READ) {
273                 bnet_fsend(dir, _("3901 Device %s is busy with 1 reader.\n"),
274                    dev_name(dev));
275             } else {
276                 bnet_fsend(dir, _("3902 Device %s is busy with %d writer(s).\n"),
277                    dev_name(dev), dev->num_writers);
278             }
279          } else {                     /* device not being used */
280             label_volume_if_ok(jcr, dev, volname, poolname);
281          }
282          V(dev->mutex);
283       } else {
284          bnet_fsend(dir, _("3999 Device %s not found\n"), dname);
285       }
286    } else {
287       /* NB dir->msg gets clobbered in bnet_fsend, so save command */
288       strcpy(dname, dir->msg);
289       bnet_fsend(dir, _("3903 Error scanning label command: %s\n"), dname);
290    }
291    free_memory(dname);
292    free_memory(volname);
293    free_memory(poolname);
294    free_memory(mtype);
295    bnet_sig(dir, BNET_EOF);
296    return 1;
297 }
298
299 /* 
300  * Read the tape label and determine if we can safely
301  * label the tape (not a Bacula volume), then label it.
302  *
303  *  Enter with the mutex set
304  */
305 static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *vname, char *poolname)
306 {
307    BSOCK *dir = jcr->dir_bsock;
308    DEV_BLOCK *block;
309    int blocked;
310    pthread_t no_wait_id;
311    
312    blocked = dev->dev_blocked;        /* save any prev blocked state */
313    no_wait_id = dev->no_wait_id;
314    dev->dev_blocked = BST_WRITING_LABEL;
315    dev->no_wait_id = pthread_self();  /* let us use the tape */
316    V(dev->mutex);                     /* release lock */
317    
318    strcpy(jcr->VolumeName, vname);
319    block = new_block(dev);
320    switch (read_dev_volume_label(jcr, dev, block)) {                
321       case VOL_NAME_ERROR:
322       case VOL_VERSION_ERROR:
323       case VOL_LABEL_ERROR:
324       case VOL_OK:
325          bnet_fsend(dir, _("3901 Cannot label Volume because it is \
326 already labeled: %s\n"), dev->VolHdr.VolName);
327          break;
328       case VOL_IO_ERROR:
329       case VOL_NO_LABEL:
330          write_volume_label_to_dev(jcr, jcr->device, vname, poolname);
331          strcpy(jcr->VolumeName, vname);
332          bnet_fsend(dir, _("3000 OK label. Volume=%s Device=%s\n"), 
333             vname, dev->dev_name);
334          break;
335       default:
336          bnet_fsend(dir, _("3902 Cannot label Volume. \
337 Unknown status %d from read_volume_label()\n"), jcr->label_status);
338          break;
339    }
340    free_block(block);
341    P(dev->mutex);
342    dev->dev_blocked = blocked;        /* reset blocked state */
343    dev->no_wait_id = no_wait_id;      /* reset blocking thread id */
344 }
345
346
347 /* 
348  * Read the tape label
349  *
350  *  Enter with the mutex set
351  */
352 static int read_label(JCR *jcr, DEVICE *dev)
353 {
354    BSOCK *dir = jcr->dir_bsock;
355    DEV_BLOCK *block;
356    int blocked;
357    pthread_t no_wait_id;
358    int stat;
359    
360    blocked = dev->dev_blocked;        /* save any prev blocked state */
361    no_wait_id = dev->no_wait_id;
362    dev->dev_blocked = BST_DOING_ACQUIRE;
363    dev->no_wait_id = pthread_self();  /* let us use the tape */
364    V(dev->mutex);                     /* release lock */
365    
366    jcr->VolumeName[0] = 0;
367    block = new_block(dev);
368    dev->state &= ~ST_LABEL;           /* force read of label */
369    switch (read_dev_volume_label(jcr, dev, block)) {                
370       case VOL_OK:
371          bnet_fsend(dir, _("3001 Mounted Volume: %s\n"), dev->VolHdr.VolName);
372          stat = 1;
373          break;
374       default:
375          bnet_fsend(dir, _("3902 Cannot mount Volume on Storage Device \"%s\" because:\n%s\n"),
376             dev->dev_name, jcr->errmsg);
377          stat = 0;
378          break;
379    }
380    free_block(block);
381    P(dev->mutex);
382    dev->dev_blocked = blocked;        /* reset blocked state */
383    dev->no_wait_id = no_wait_id;      /* reset blocking thread id */
384    return stat;
385 }
386
387 /*
388  * Mount command from Director
389  */
390 static int mount_cmd(JCR *jcr)
391 {
392    char *dev_name;
393    BSOCK *dir = jcr->dir_bsock;
394    DEVRES *device;
395    DEVICE *dev;
396    int found = 0;
397
398    dev_name = (char *) get_memory(dir->msglen);
399    if (sscanf(dir->msg, "mount %s", dev_name) == 1) {
400       unbash_spaces(dev_name);
401       device = NULL;
402       LockRes();
403       while ((device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device))) {
404          /* Find resource, and make sure we were able to open it */
405          if (strcmp(device->hdr.name, dev_name) == 0 && device->dev) {
406             Dmsg1(20, "Found device %s\n", device->hdr.name);
407             found = 1;
408             break;
409          }
410       }
411       UnlockRes();
412       if (found) {
413          jcr->device = device;
414          dev = device->dev;
415          P(dev->mutex);
416          switch (dev->dev_blocked) {         /* device blocked? */
417             DEV_BLOCK *block;
418             case BST_WAITING_FOR_SYSOP:
419                /* Someone is waiting, wake him */
420                Dmsg0(90, "Waiting for mount attempt to wake thread\n");
421                pthread_cond_signal(&dev->wait_next_vol);
422                bnet_fsend(dir, "3001 OK mount. Device=%s\n", dev->dev_name);
423                break;
424
425             case BST_UNMOUNTED_WAITING_FOR_SYSOP:
426             case BST_UNMOUNTED:
427                /* We freed the device, so reopen it and wake any waiting threads */
428                if (open_dev(dev, NULL, READ_WRITE) < 0) {
429                   bnet_fsend(dir, _("3901 open device failed: ERR=%s\n"), 
430                      strerror_dev(dev));
431                   break;
432                }
433                block = new_block(dev);
434                read_dev_volume_label(jcr, dev, block);
435                free_block(block);
436                if (dev->dev_blocked == BST_UNMOUNTED) {
437                   Dmsg0(90, "Unmounted unblocking device\n");
438                   read_label(jcr, dev);
439                   unblock_device(dev);
440                } else {
441                   Dmsg0(90, "Unmounted waiting for mount attempt to wake thread\n");
442                   dev->dev_blocked = BST_WAITING_FOR_SYSOP;
443                   pthread_cond_signal(&dev->wait_next_vol);
444                }
445                if (dev->state & ST_LABEL) {
446                   bnet_fsend(dir, _("3001 Device %s is mounted with Volume %s\n"), 
447                      dev->dev_name, dev->VolHdr.VolName);
448                } else {
449                   bnet_fsend(dir, _("3905 Device %s open but no Bacula volume is mounted.\n"), 
450                              dev->dev_name);
451                }
452                break;
453
454             case BST_DOING_ACQUIRE:
455                bnet_fsend(dir, _("3001 Device %s is mounted; doing acquire.\n"), 
456                           dev->dev_name);
457                break;
458
459             case BST_WRITING_LABEL:
460                bnet_fsend(dir, _("3903 Device %s is being labeled.\n"), dev->dev_name);
461                break;
462
463             case BST_NOT_BLOCKED:
464                if (dev->state & ST_OPENED) {
465                   if (dev->state & ST_LABEL) {
466                      bnet_fsend(dir, _("3001 Device %s is mounted with Volume %s\n"),
467                         dev->dev_name, dev->VolHdr.VolName);
468                   } else {
469                      bnet_fsend(dir, _("3905 Device %s open but no Bacula volume is mounted.\n"), 
470                                 dev->dev_name);
471                   }
472                } else {
473                   if (!dev_is_tape(dev)) {
474                      bnet_fsend(dir, _("3906 cannot mount non-tape.\n"));
475                      break;
476                   }
477                   if (open_dev(dev, NULL, READ_WRITE) < 0) {
478                      bnet_fsend(dir, _("3901 open device failed: ERR=%s\n"), 
479                         strerror_dev(dev));
480                      break;
481                   }
482                   read_label(jcr, dev);
483                   if (dev->state & ST_LABEL) {
484                      bnet_fsend(dir, _("3001 Device %s is mounted with Volume %s\n"), 
485                         dev->dev_name, dev->VolHdr.VolName);
486                   } else {
487                      bnet_fsend(dir, _("3905 Device %s open but no Bacula volume is mounted.\n"), 
488                                 dev->dev_name);
489                   }
490                }
491                break;
492
493             default:
494                bnet_fsend(dir, _("3905 Bizarre wait state %d\n"), dev->dev_blocked);
495                break;
496          }
497          V(dev->mutex);
498       } else {
499          bnet_fsend(dir, _("3999 Device %s not found\n"), dev_name);
500       }
501    } else {
502       strcpy(dev_name, dir->msg);
503       bnet_fsend(dir, _("3906 Error scanning mount command: %s\n"), dev_name);
504    }
505    free_memory(dev_name);
506    bnet_sig(dir, BNET_EOF);
507    return 1;
508 }
509
510 /*
511  * unmount command from Director
512  */
513 static int unmount_cmd(JCR *jcr)
514 {
515    char *dname;
516    BSOCK *dir = jcr->dir_bsock;
517    DEVRES *device;
518    DEVICE *dev;
519    int found = 0;
520
521    dname = (char *) get_memory(dir->msglen+1);
522    if (sscanf(dir->msg, "unmount %s", dname) == 1) {
523       unbash_spaces(dname);
524       device = NULL;
525       LockRes();
526       while ((device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device))) {
527          /* Find resource, and make sure we were able to open it */
528          if (strcmp(device->hdr.name, dname) == 0 && device->dev) {
529             Dmsg1(20, "Found device %s\n", device->hdr.name);
530             found = 1;
531             break;
532          }
533       }
534       UnlockRes();
535       if (found) {
536          jcr->device = device;
537          dev = device->dev;
538          P(dev->mutex);
539          if (!(dev->state & ST_OPENED)) {
540             Dmsg0(90, "Device already unmounted\n");
541             bnet_fsend(dir, _("3901 Device %s is already unmounted.\n"), dev_name(dev));
542
543          } else if (dev->dev_blocked == BST_WAITING_FOR_SYSOP) {
544             Dmsg2(90, "%d waiter dev_block=%d. doing unmount\n", dev->num_waiting,
545                dev->dev_blocked);
546             force_close_dev(dev);
547             dev->dev_blocked = BST_UNMOUNTED_WAITING_FOR_SYSOP;
548             bnet_fsend(dir, _("3001 Device %s unmounted.\n"), dev_name(dev));
549
550          } else if (dev->dev_blocked == BST_DOING_ACQUIRE) {
551             bnet_fsend(dir, _("3902 Device %s is busy in acquire.\n"),
552                dev_name(dev));
553
554          } else if (dev->dev_blocked == BST_WRITING_LABEL) {
555             bnet_fsend(dir, _("3903 Device %s is being labeled.\n"),
556                dev_name(dev));
557
558          } else if (dev->state & ST_READ || dev->num_writers) {
559             if (dev->state & ST_READ) {
560                 Dmsg0(90, "Device in read mode\n");
561                 bnet_fsend(dir, _("3904 Device %s is busy with 1 reader.\n"),
562                    dev_name(dev));
563             } else {
564                 Dmsg1(90, "Device busy with %d writers\n", dev->num_writers);
565                 bnet_fsend(dir, _("3905 Device %s is busy with %d writer(s).\n"),
566                    dev_name(dev), dev->num_writers);
567             }
568
569          } else {                     /* device not being used */
570             Dmsg0(90, "Device not in use, unmounting\n");
571             block_device(dev, BST_UNMOUNTED);
572             force_close_dev(dev);
573             bnet_fsend(dir, _("3002 Device %s unmounted.\n"), dev_name(dev));
574          }
575          V(dev->mutex);
576       } else {
577          bnet_fsend(dir, _("3999 Device %s not found\n"), dname);
578       }
579    } else {
580       /* NB dir->msg gets clobbered in bnet_fsend, so save command */
581       strcpy(dname, dir->msg);
582       bnet_fsend(dir, _("3907 Error scanning unmount command: %s\n"), dname);
583    }
584    free_memory(dname);
585    bnet_sig(dir, BNET_EOF);
586    return 1;
587 }
588
589 /*
590  * Status command from Director
591  */
592 static int status_cmd(JCR *jcr)
593 {
594    DEVRES *device;
595    DEVICE *dev;
596    int found, bps, sec, bpb;
597    BSOCK *user = jcr->dir_bsock;
598    char dt[MAX_TIME_LENGTH];
599    char b1[30], b2[30], b3[30];
600
601    bnet_fsend(user, "\n%s Version: " VERSION " (" DATE ")\n", my_name);
602    bstrftime(dt, sizeof(dt), daemon_start_time);
603    bnet_fsend(user, _("Daemon started %s, %d Job%s run.\n"), dt, last_job.NumJobs,
604         last_job.NumJobs == 1 ? "" : "s");
605    if (last_job.NumJobs > 0) {
606       char *termstat, jstat[2];
607
608       bstrftime(dt, sizeof(dt), last_job.end_time);
609       bnet_fsend(user, _("Last Job %s finished at %s\n"), last_job.Job, dt);
610       switch (last_job.JobStatus) {
611          case JS_Terminated:
612             termstat = _("OK");
613             break;
614         case JS_ErrorTerminated:
615             termstat = _("Error");
616             break;
617         default:
618             jstat[0] = last_job.JobStatus;
619             jstat[1] = 0;
620             termstat = jstat;
621             break;
622       }
623            
624       bnet_fsend(user, _("  Files=%s Bytes=%s Termination Status=%s\n"), 
625            edit_uint64_with_commas(last_job.JobFiles, b1),
626            edit_uint64_with_commas(last_job.JobBytes, b2),
627            termstat);
628    }
629
630    LockRes();
631    for (device=NULL;  (device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device)); ) {
632       dev = device->dev;
633       if (dev) {
634          if (dev->state & ST_OPENED) {
635             if (dev->state & ST_LABEL) {
636                bnet_fsend(user, _("Device %s is mounted with Volume %s\n"), 
637                   dev_name(dev), dev->VolHdr.VolName);
638             } else {
639                bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"), dev_name(dev));
640             }
641             switch (dev->dev_blocked) {
642                case BST_UNMOUNTED:
643                   bnet_fsend(user, _("    Deviced is blocked. User unmounted.\n"));
644                   break;
645                case BST_UNMOUNTED_WAITING_FOR_SYSOP:
646                   bnet_fsend(user, _("    Deviced is blocked. User unmounted during wait for media/mount.\n"));
647                   break;
648                case BST_WAITING_FOR_SYSOP:
649                   if (jcr->JobStatus == JS_WaitMount) {
650                      bnet_fsend(user, _("    Device is blocked waiting for mount.\n"));
651                   } else {
652                      bnet_fsend(user, _("    Device is blocked waiting for appendable media.\n"));
653                   }
654                   break;
655                case BST_DOING_ACQUIRE:
656                   bnet_fsend(user, _("    Device is being initialized.\n"));
657                   break;
658                case BST_WRITING_LABEL:
659                   bnet_fsend(user, _("    Device is blocked labeling a Volume.\n"));
660                   break;
661                default:
662                   break;
663             }
664             bpb = dev->VolCatInfo.VolCatBlocks;
665             if (bpb <= 0) {
666                bpb = 1;
667             }
668             bpb = dev->VolCatInfo.VolCatBytes / bpb;
669             bnet_fsend(user, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
670                edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
671                edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2), 
672                edit_uint64_with_commas(bpb, b3));
673             bnet_fsend(user, _("    Positioned at File=%s Block=%s\n"), 
674                edit_uint64_with_commas(dev->file, b1),
675                edit_uint64_with_commas(dev->block_num, b2));
676
677          } else {
678             bnet_fsend(user, _("Device %s is not open.\n"), dev_name(dev));
679          }
680       }
681    }
682    UnlockRes();
683
684    found = 0;
685    lock_jcr_chain();
686    /* NOTE, we reuse a calling argument jcr. Be warned! */ 
687    for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
688       if (jcr->JobStatus == JS_WaitFD) {
689          bnet_fsend(user, _("Job %s is waiting for the Client connection.\n"),
690             jcr->Job);
691       }
692       if (jcr->device) {
693          bnet_fsend(user, _("Job %s is using device %s\n"), 
694                    jcr->Job, jcr->device->device_name);
695          sec = time(NULL) - jcr->run_time;
696          if (sec <= 0) {
697             sec = 1;
698          }
699          bps = jcr->JobBytes / sec;
700          bnet_fsend(user, _("    Files=%s Bytes=%s Bytes/sec=%s\n"), 
701             edit_uint64_with_commas(jcr->JobFiles, b1),
702             edit_uint64_with_commas(jcr->JobBytes, b2),
703             edit_uint64_with_commas(bps, b3));
704          found = 1;
705 #ifdef DEBUG
706          if (jcr->file_bsock) {
707             bnet_fsend(user, "    FDReadSeqNo=%" lld " fd=%d\n", 
708                jcr->file_bsock->read_seqno, jcr->file_bsock->fd);
709          } else {
710             bnet_fsend(user, "    FDSocket closed\n");
711          }
712 #endif
713       }
714       free_locked_jcr(jcr);
715    }
716    unlock_jcr_chain();
717    if (!found) {
718       bnet_fsend(user, _("No jobs running.\n"));
719    }
720
721 #ifdef full_status
722    bnet_fsend(user, "\n\n");
723    dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
724 #endif
725    bnet_fsend(user, "====\n");
726
727    bnet_sig(user, BNET_EOF);
728    return 1;
729 }