]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_update.c
c02ab2bca41cbfc8e22e499c3f3104aeea7ed749
[bacula/bacula] / bacula / src / dird / ua_update.c
1 /*
2  *
3  *   Bacula Director -- Update command processing
4  *     Split from ua_cmds.c March 2005
5  *
6  *     Kern Sibbald, September MM
7  *
8  *   Version $Id$
9  */
10 /*
11    Copyright (C) 2000-2006 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25 #include "bacula.h"
26 #include "dird.h"
27
28 /* External variables */
29 extern char *list_pool;               /* in sql_cmds.c */
30
31 /* Imported functions */
32 void update_slots(UAContext *ua);
33
34
35
36 /* Forward referenced functions */
37 static int update_volume(UAContext *ua);
38 static int update_pool(UAContext *ua);
39
40 /*
41  * Update a Pool Record in the database.
42  *  It is always updated from the Resource record.
43  *
44  *    update pool=<pool-name>
45  *         updates pool from Pool resource
46  *    update media pool=<pool-name> volume=<volume-name>
47  *         changes pool info for volume
48  *    update slots [scan=...]
49  *         updates autochanger slots
50  */
51 int update_cmd(UAContext *ua, const char *cmd)
52 {
53    static const char *kw[] = {
54       N_("media"),  /* 0 */
55       N_("volume"), /* 1 */
56       N_("pool"),   /* 2 */
57       N_("slots"),  /* 3 */
58       NULL};
59
60    if (!open_db(ua)) {
61       return 1;
62    }
63
64    switch (find_arg_keyword(ua, kw)) {
65    case 0:
66    case 1:
67       update_volume(ua);
68       return 1;
69    case 2:
70       update_pool(ua);
71       return 1;
72    case 3:
73       update_slots(ua);
74       return 1;
75    default:
76       break;
77    }
78
79    start_prompt(ua, _("Update choice:\n"));
80    add_prompt(ua, _("Volume parameters"));
81    add_prompt(ua, _("Pool from resource"));
82    add_prompt(ua, _("Slots from autochanger"));
83    switch (do_prompt(ua, _("item"), _("Choose catalog item to update"), NULL, 0)) {
84    case 0:
85       update_volume(ua);
86       break;
87    case 1:
88       update_pool(ua);
89       break;
90    case 2:
91       update_slots(ua);
92       break;
93    default:
94       break;
95    }
96    return 1;
97 }
98
99 static void update_volstatus(UAContext *ua, const char *val, MEDIA_DBR *mr)
100 {
101    POOL_MEM query(PM_MESSAGE);
102    const char *kw[] = {
103       N_("Append"),
104       N_("Archive"),
105       N_("Disabled"),
106       N_("Full"),
107       N_("Used"),
108       N_("Cleaning"),
109       N_("Recycle"),
110       N_("Read-Only"),
111       NULL};
112    bool found = false;
113    int i;
114
115    for (i=0; kw[i]; i++) {
116       if (strcasecmp(val, kw[i]) == 0) {
117          found = true;
118          break;
119       }
120    }
121    if (!found) {
122       bsendmsg(ua, _("Invalid VolStatus specified: %s\n"), val);
123    } else {
124       char ed1[50];
125       bstrncpy(mr->VolStatus, kw[i], sizeof(mr->VolStatus));
126       Mmsg(query, "UPDATE Media SET VolStatus='%s' WHERE MediaId=%s",
127          mr->VolStatus, edit_int64(mr->MediaId,ed1));
128       if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
129          bsendmsg(ua, "%s", db_strerror(ua->db));
130       } else {
131          bsendmsg(ua, _("New Volume status is: %s\n"), mr->VolStatus);
132       }
133    }
134 }
135
136 static void update_volretention(UAContext *ua, char *val, MEDIA_DBR *mr)
137 {
138    char ed1[150], ed2[50];
139    POOL_MEM query(PM_MESSAGE);
140    if (!duration_to_utime(val, &mr->VolRetention)) {
141       bsendmsg(ua, _("Invalid retention period specified: %s\n"), val);
142       return;
143    }
144    Mmsg(query, "UPDATE Media SET VolRetention=%s WHERE MediaId=%s",
145       edit_uint64(mr->VolRetention, ed1), edit_int64(mr->MediaId,ed2));
146    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
147       bsendmsg(ua, "%s", db_strerror(ua->db));
148    } else {
149       bsendmsg(ua, _("New retention period is: %s\n"),
150          edit_utime(mr->VolRetention, ed1, sizeof(ed1)));
151    }
152 }
153
154 static void update_voluseduration(UAContext *ua, char *val, MEDIA_DBR *mr)
155 {
156    char ed1[150], ed2[50];
157    POOL_MEM query(PM_MESSAGE);
158
159    if (!duration_to_utime(val, &mr->VolUseDuration)) {
160       bsendmsg(ua, _("Invalid use duration specified: %s\n"), val);
161       return;
162    }
163    Mmsg(query, "UPDATE Media SET VolUseDuration=%s WHERE MediaId=%s",
164       edit_uint64(mr->VolUseDuration, ed1), edit_int64(mr->MediaId,ed2));
165    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
166       bsendmsg(ua, "%s", db_strerror(ua->db));
167    } else {
168       bsendmsg(ua, _("New use duration is: %s\n"),
169          edit_utime(mr->VolUseDuration, ed1, sizeof(ed1)));
170    }
171 }
172
173 static void update_volmaxjobs(UAContext *ua, char *val, MEDIA_DBR *mr)
174 {
175    POOLMEM *query = get_pool_memory(PM_MESSAGE);
176    char ed1[50];
177    Mmsg(query, "UPDATE Media SET MaxVolJobs=%s WHERE MediaId=%s",
178       val, edit_int64(mr->MediaId,ed1));
179    if (!db_sql_query(ua->db, query, NULL, NULL)) {
180       bsendmsg(ua, "%s", db_strerror(ua->db));
181    } else {
182       bsendmsg(ua, _("New max jobs is: %s\n"), val);
183    }
184    free_pool_memory(query);
185 }
186
187 static void update_volmaxfiles(UAContext *ua, char *val, MEDIA_DBR *mr)
188 {
189    POOLMEM *query = get_pool_memory(PM_MESSAGE);
190    char ed1[50];
191    Mmsg(query, "UPDATE Media SET MaxVolFiles=%s WHERE MediaId=%s",
192       val, edit_int64(mr->MediaId, ed1));
193    if (!db_sql_query(ua->db, query, NULL, NULL)) {
194       bsendmsg(ua, "%s", db_strerror(ua->db));
195    } else {
196       bsendmsg(ua, _("New max files is: %s\n"), val);
197    }
198    free_pool_memory(query);
199 }
200
201 static void update_volmaxbytes(UAContext *ua, char *val, MEDIA_DBR *mr)
202 {
203    uint64_t maxbytes;
204    char ed1[50], ed2[50];
205    POOLMEM *query;
206
207    if (!size_to_uint64(val, strlen(val), &maxbytes)) {
208       bsendmsg(ua, _("Invalid max. bytes specification: %s\n"), val);
209       return;
210    }
211    query = get_pool_memory(PM_MESSAGE);
212    Mmsg(query, "UPDATE Media SET MaxVolBytes=%s WHERE MediaId=%s",
213       edit_uint64(maxbytes, ed1), edit_int64(mr->MediaId, ed2));
214    if (!db_sql_query(ua->db, query, NULL, NULL)) {
215       bsendmsg(ua, "%s", db_strerror(ua->db));
216    } else {
217       bsendmsg(ua, _("New Max bytes is: %s\n"), edit_uint64(maxbytes, ed1));
218    }
219    free_pool_memory(query);
220 }
221
222 static void update_volrecycle(UAContext *ua, char *val, MEDIA_DBR *mr)
223 {
224    int recycle;
225    char ed1[50];
226    POOLMEM *query;
227    if (strcasecmp(val, _("yes")) == 0) {
228       recycle = 1;
229    } else if (strcasecmp(val, _("no")) == 0) {
230       recycle = 0;
231    } else {
232       bsendmsg(ua, _("Invalid value. It must by yes or no.\n"));
233       return;
234    }
235    query = get_pool_memory(PM_MESSAGE);
236    Mmsg(query, "UPDATE Media SET Recycle=%d WHERE MediaId=%s",
237       recycle, edit_int64(mr->MediaId, ed1));
238    if (!db_sql_query(ua->db, query, NULL, NULL)) {
239       bsendmsg(ua, "%s", db_strerror(ua->db));
240    } else {
241       bsendmsg(ua, _("New Recycle flag is: %s\n"),
242          mr->Recycle==1?_("yes"):_("no"));
243    }
244    free_pool_memory(query);
245 }
246
247 /* Modify the Pool in which this Volume is located */
248 static void update_vol_pool(UAContext *ua, char *val, MEDIA_DBR *mr, POOL_DBR *opr)
249 {
250    POOL_DBR pr;
251    POOLMEM *query;
252    char ed1[50], ed2[50];
253
254    memset(&pr, 0, sizeof(pr));
255    bstrncpy(pr.Name, val, sizeof(pr.Name));
256    if (!get_pool_dbr(ua, &pr)) {
257       return;
258    }
259    mr->PoolId = pr.PoolId;            /* set new PoolId */
260    /*
261     */
262    query = get_pool_memory(PM_MESSAGE);
263    db_lock(ua->db);
264    Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
265       edit_int64(mr->PoolId, ed1),
266       edit_int64(mr->MediaId, ed2));
267    if (!db_sql_query(ua->db, query, NULL, NULL)) {
268       bsendmsg(ua, "%s", db_strerror(ua->db));
269    } else {
270       bsendmsg(ua, _("New Pool is: %s\n"), pr.Name);
271       opr->NumVols--;
272       if (!db_update_pool_record(ua->jcr, ua->db, opr)) {
273          bsendmsg(ua, "%s", db_strerror(ua->db));
274       }
275       pr.NumVols++;
276       if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
277          bsendmsg(ua, "%s", db_strerror(ua->db));
278       }
279    }
280    db_unlock(ua->db);
281    free_pool_memory(query);
282 }
283
284 /*
285  * Refresh the Volume information from the Pool record
286  */
287 static void update_vol_from_pool(UAContext *ua, MEDIA_DBR *mr)
288 {
289    POOL_DBR pr;
290
291    memset(&pr, 0, sizeof(pr));
292    pr.PoolId = mr->PoolId;
293    if (!db_get_pool_record(ua->jcr, ua->db, &pr) ||
294        !acl_access_ok(ua, Pool_ACL, pr.Name)) {
295       return;
296    }
297    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
298    if (!db_update_media_defaults(ua->jcr, ua->db, mr)) {
299       bsendmsg(ua, _("Error updating Volume record: ERR=%s"), db_strerror(ua->db));
300    } else {
301       bsendmsg(ua, _("Volume defaults updated from \"%s\" Pool record.\n"),
302          pr.Name);
303    }
304 }
305
306 /*
307  * Refresh the Volume information from the Pool record
308  *   for all Volumes
309  */
310 static void update_all_vols_from_pool(UAContext *ua)
311 {
312    POOL_DBR pr;
313    MEDIA_DBR mr;
314
315    memset(&pr, 0, sizeof(pr));
316    memset(&mr, 0, sizeof(mr));
317    if (!get_pool_dbr(ua, &pr)) {
318       return;
319    }
320    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
321    mr.PoolId = pr.PoolId;
322    if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) {
323       bsendmsg(ua, _("Error updating Volume records: ERR=%s"), db_strerror(ua->db));
324    } else {
325       bsendmsg(ua, _("All Volume defaults updated from Pool record.\n"));
326    }
327 }
328
329
330 /*
331  * Update a media record -- allows you to change the
332  *  Volume status. E.g. if you want Bacula to stop
333  *  writing on the volume, set it to anything other
334  *  than Append.
335  */
336 static int update_volume(UAContext *ua)
337 {
338    MEDIA_DBR mr;
339    POOL_DBR pr;
340    POOLMEM *query;
341    char ed1[130];
342    bool done = false;
343    int i;
344    const char *kw[] = {
345       _("VolStatus"),                /* 0 */
346       _("VolRetention"),             /* 1 */
347       _("VolUse"),                   /* 2 */
348       _("MaxVolJobs"),               /* 3 */
349       _("MaxVolFiles"),              /* 4 */
350       _("MaxVolBytes"),              /* 5 */
351       _("Recycle"),                  /* 6 */
352       _("Pool"),                     /* 7 */
353       _("FromPool"),                 /* 8 */
354       _("AllFromPool"),              /* 9 */
355       NULL };
356
357    for (i=0; kw[i]; i++) {
358       int j;
359       POOL_DBR pr;
360       if ((j=find_arg_with_value(ua, kw[i])) > 0) {
361          if (i != 9 && !select_media_dbr(ua, &mr)) {
362             return 0;
363          }
364          switch (i) {
365          case 0:
366             update_volstatus(ua, ua->argv[j], &mr);
367             break;
368          case 1:
369             update_volretention(ua, ua->argv[j], &mr);
370             break;
371          case 2:
372             update_voluseduration(ua, ua->argv[j], &mr);
373             break;
374          case 3:
375             update_volmaxjobs(ua, ua->argv[j], &mr);
376             break;
377          case 4:
378             update_volmaxfiles(ua, ua->argv[j], &mr);
379             break;
380          case 5:
381             update_volmaxbytes(ua, ua->argv[j], &mr);
382             break;
383          case 6:
384             update_volrecycle(ua, ua->argv[j], &mr);
385             break;
386          case 7:
387             memset(&pr, 0, sizeof(POOL_DBR));
388             pr.PoolId = mr.PoolId;
389             if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
390                bsendmsg(ua, "%s", db_strerror(ua->db));
391                break;
392             }
393             update_vol_pool(ua, ua->argv[j], &mr, &pr);
394             break;
395          case 8:
396             update_vol_from_pool(ua, &mr);
397             return 1;
398          case 9:
399             update_all_vols_from_pool(ua);
400             return 1;
401          }
402          done = true;
403       }
404    }
405
406    for ( ; !done; ) {
407       bsendmsg(ua, _("Updating Volume \"%s\"\n"), mr.VolumeName);
408       start_prompt(ua, _("Parameters to modify:\n"));
409       add_prompt(ua, _("Volume Status"));
410       add_prompt(ua, _("Volume Retention Period"));
411       add_prompt(ua, _("Volume Use Duration"));
412       add_prompt(ua, _("Maximum Volume Jobs"));
413       add_prompt(ua, _("Maximum Volume Files"));
414       add_prompt(ua, _("Maximum Volume Bytes"));
415       add_prompt(ua, _("Recycle Flag"));
416       add_prompt(ua, _("Slot"));
417       add_prompt(ua, _("InChanger Flag"));
418       add_prompt(ua, _("Volume Files"));
419       add_prompt(ua, _("Pool"));
420       add_prompt(ua, _("Volume from Pool"));
421       add_prompt(ua, _("All Volumes from Pool"));
422       add_prompt(ua, _("Done"));
423       i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);  
424       /* For All Volumes from Pool we don't need a Volume record */
425       if (i != 12) {
426          if (!select_media_dbr(ua, &mr)) {  /* Get Volume record */
427             return 0;
428          }
429       }
430       switch (i) {
431       case 0:                         /* Volume Status */
432          /* Modify Volume Status */
433          bsendmsg(ua, _("Current Volume status is: %s\n"), mr.VolStatus);
434          start_prompt(ua, _("Possible Values are:\n"));
435          add_prompt(ua, N_("Append")); 
436          add_prompt(ua, N_("Archive"));
437          add_prompt(ua, N_("Disabled"));
438          add_prompt(ua, N_("Full"));
439          add_prompt(ua, N_("Used"));
440          add_prompt(ua, N_("Cleaning"));
441          if (strcmp(mr.VolStatus, N_("Purged")) == 0) {
442             add_prompt(ua, N_("Recycle"));
443          }
444          add_prompt(ua, N_("Read-Only"));
445          if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) {
446             return 1;
447          }
448          update_volstatus(ua, ua->cmd, &mr);
449          break;
450       case 1:                         /* Retention */
451          bsendmsg(ua, _("Current retention period is: %s\n"),
452             edit_utime(mr.VolRetention, ed1, sizeof(ed1)));
453          if (!get_cmd(ua, _("Enter Volume Retention period: "))) {
454             return 0;
455          }
456          update_volretention(ua, ua->cmd, &mr);
457          break;
458
459       case 2:                         /* Use Duration */
460          bsendmsg(ua, _("Current use duration is: %s\n"),
461             edit_utime(mr.VolUseDuration, ed1, sizeof(ed1)));
462          if (!get_cmd(ua, _("Enter Volume Use Duration: "))) {
463             return 0;
464          }
465          update_voluseduration(ua, ua->cmd, &mr);
466          break;
467
468       case 3:                         /* Max Jobs */
469          bsendmsg(ua, _("Current max jobs is: %u\n"), mr.MaxVolJobs);
470          if (!get_pint(ua, _("Enter new Maximum Jobs: "))) {
471             return 0;
472          }
473          update_volmaxjobs(ua, ua->cmd, &mr);
474          break;
475
476       case 4:                         /* Max Files */
477          bsendmsg(ua, _("Current max files is: %u\n"), mr.MaxVolFiles);
478          if (!get_pint(ua, _("Enter new Maximum Files: "))) {
479             return 0;
480          }
481          update_volmaxfiles(ua, ua->cmd, &mr);
482          break;
483
484       case 5:                         /* Max Bytes */
485          bsendmsg(ua, _("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1));
486          if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) {
487             return 0;
488          }
489          update_volmaxbytes(ua, ua->cmd, &mr);
490          break;
491
492
493       case 6:                         /* Recycle */
494          bsendmsg(ua, _("Current recycle flag is: %s\n"),
495             mr.Recycle==1?_("yes"):_("no"));
496          if (!get_yesno(ua, _("Enter new Recycle status: "))) {
497             return 0;
498          }
499          update_volrecycle(ua, ua->cmd, &mr);
500          break;
501
502       case 7:                         /* Slot */
503          int Slot;
504
505          memset(&pr, 0, sizeof(POOL_DBR));
506          pr.PoolId = mr.PoolId;
507          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
508             bsendmsg(ua, "%s", db_strerror(ua->db));
509             return 0;
510          }
511          bsendmsg(ua, _("Current Slot is: %d\n"), mr.Slot);
512          if (!get_pint(ua, _("Enter new Slot: "))) {
513             return 0;
514          }
515          Slot = ua->pint32_val;
516          if (pr.MaxVols > 0 && Slot > (int)pr.MaxVols) {
517             bsendmsg(ua, _("Invalid slot, it must be between 0 and MaxVols=%d\n"),
518                pr.MaxVols);
519             break;
520          }
521          mr.Slot = Slot;
522          /*
523           * Make sure to use db_update... rather than doing this directly,
524           *   so that any Slot is handled correctly.
525           */
526          if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
527             bsendmsg(ua, _("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
528          } else {
529             bsendmsg(ua, _("New Slot is: %d\n"), mr.Slot);
530          }
531          break;
532
533       case 8:                         /* InChanger */
534          bsendmsg(ua, _("Current InChanger flag is: %d\n"), mr.InChanger);
535          if (!get_yesno(ua, _("Set InChanger flag? yes/no: "))) {
536             return 0;
537          }
538          mr.InChanger = ua->pint32_val;
539          /*
540           * Make sure to use db_update... rather than doing this directly,
541           *   so that any Slot is handled correctly.
542           */
543          if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
544             bsendmsg(ua, _("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
545          } else {
546             bsendmsg(ua, _("New InChanger flag is: %d\n"), mr.InChanger);
547          }
548          break;
549
550
551       case 9:                         /* Volume Files */
552          int32_t VolFiles;
553          bsendmsg(ua, _("Warning changing Volume Files can result\n"
554                         "in loss of data on your Volume\n\n"));
555          bsendmsg(ua, _("Current Volume Files is: %u\n"), mr.VolFiles);
556          if (!get_pint(ua, _("Enter new number of Files for Volume: "))) {
557             return 0;
558          }
559          VolFiles = ua->pint32_val;
560          if (VolFiles != (int)(mr.VolFiles + 1)) {
561             bsendmsg(ua, _("Normally, you should only increase Volume Files by one!\n"));
562             if (!get_yesno(ua, _("Continue? (yes/no): ")) || ua->pint32_val == 0) {
563                break;
564             }
565          }
566          query = get_pool_memory(PM_MESSAGE);
567          Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s",
568             VolFiles, edit_int64(mr.MediaId, ed1));
569          if (!db_sql_query(ua->db, query, NULL, NULL)) {
570             bsendmsg(ua, "%s", db_strerror(ua->db));
571          } else {
572             bsendmsg(ua, _("New Volume Files is: %u\n"), VolFiles);
573          }
574          free_pool_memory(query);
575          break;
576
577       case 10:                        /* Volume's Pool */
578          memset(&pr, 0, sizeof(POOL_DBR));
579          pr.PoolId = mr.PoolId;
580          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
581             bsendmsg(ua, "%s", db_strerror(ua->db));
582             return 0;
583          }
584          bsendmsg(ua, _("Current Pool is: %s\n"), pr.Name);
585          if (!get_cmd(ua, _("Enter new Pool name: "))) {
586             return 0;
587          }
588          update_vol_pool(ua, ua->cmd, &mr, &pr);
589          return 1;
590
591       case 11:
592          update_vol_from_pool(ua, &mr);
593          return 1;
594       case 12:
595          update_all_vols_from_pool(ua);
596          return 1;
597       default:                        /* Done or error */
598          bsendmsg(ua, _("Selection terminated.\n"));
599          return 1;
600       }
601    }
602    return 1;
603 }
604
605 /*
606  * Update pool record -- pull info from current POOL resource
607  */
608 static int update_pool(UAContext *ua)
609 {
610    POOL_DBR  pr;
611    int id;
612    POOL *pool;
613    POOLMEM *query;
614    char ed1[50];
615
616    pool = get_pool_resource(ua);
617    if (!pool) {
618       return 0;
619    }
620
621    memset(&pr, 0, sizeof(pr));
622    bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
623    if (!get_pool_dbr(ua, &pr)) {
624       return 0;
625    }
626
627    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE); /* update */
628
629    id = db_update_pool_record(ua->jcr, ua->db, &pr);
630    if (id <= 0) {
631       bsendmsg(ua, _("db_update_pool_record returned %d. ERR=%s\n"),
632          id, db_strerror(ua->db));
633    }
634    query = get_pool_memory(PM_MESSAGE);
635    Mmsg(query, list_pool, edit_int64(pr.PoolId, ed1));
636    db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, HORZ_LIST);
637    free_pool_memory(query);
638    bsendmsg(ua, _("Pool DB record updated from resource.\n"));
639    return 1;
640 }