1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/sysmacros.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <sys/statvfs.h>
41 #include <signal.h>
42 #include <limits.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <time.h>
48 #include <errno.h>
49 #include <pkglocs.h>
50 #include <locale.h>
51 #include <libintl.h>
52 #include <pkglib.h>
53 #include "libinst.h"
54 #include "libadm.h"
55
56 #define LOCKFILE ".pkg.lock.client"
57 #define LOCKFILESERV ".pkg.lock"
58
59 #define LOCKWAIT 10 /* seconds between retries */
60 #define LOCKRETRY 20 /* number of retries for a DB lock */
61
62 #define ERR_COMMIT "WARNING: unable to commit contents database update"
63 #define ERR_NOCLOSE "WARNING: unable to close <%s>"
64 #define ERR_NOUNLINK_LATENT "WARNING: unable to unlink latent <%s>"
65 #define ERR_LINK_FAIL "link(%s, %s) failed (errno %d)"
66 #define ERR_NORENAME_CONTENTS "unable to establish contents file <%s> "\
67 "from <%s>"
68 #define ERR_RENAME_FAIL "rename(%s, %s) failed (errno %d)"
69 #define ERR_RESTORE_FAIL "attempt to restore <%s> failed"
70 #define ERR_NOUNLINK "WARNING: unable to unlink <%s>"
71 #define ERR_FCLOSE_FAIL "fclose failed (errno %d)"
72 #define ERR_ERRNO "(errno %d: %s)"
73 #define ERR_NOTMPOPEN "unable to open temporary contents file image"
74 #define ERR_CFBACK "Not enough space to backup <%s>"
75 #define ERR_CREAT_CONT "unable to create contents file <%s>: %s"
76 #define ERR_ACCESS_CONT "unable to access contents file <%s>: %s"
77 #define ERR_CFBACK1 "Need=%llu blocks, Available=%llu blocks " \
78 "(block size=%d)"
79 #define ERR_NOCFILE "unable to locate contents file <%s>"
80 #define ERR_NOROPEN "unable to open <%s> for reading"
81 #define ERR_NOOPEN "unable to open <%s> for writing"
82 #define ERR_NOSTAT "unable to stat contents file <%s>"
83 #define ERR_NOSTATV "statvfs(%s) failed"
84 #define ERR_NOUPD "unable to update contents file"
85 #define ERR_DRCONTCP "unable to copy contents file to <%s>"
86
87 #define MSG_XWTING "NOTE: Waiting for exclusive access to the package " \
88 "database."
89 #define MSG_NOLOCK "NOTE: Couldn't lock the package database."
90
91 #define ERR_NOLOCK "Database lock failed."
92 #define ERR_OPLOCK "unable to open lock file <%s>."
93 #define ERR_MKLOCK "unable to create lock file <%s>."
94 #define ERR_LCKREM "unable to lock package database - remote host " \
95 "unavailable."
96 #define ERR_BADLCK "unable to lock package database - unknown error."
97 #define ERR_DEADLCK "unable to lock package database - deadlock condition."
98 #define ERR_TMOUT "unable to lock package database - too many retries."
99 #define ERR_CFDIR "unable to locate contents file directory"
100
101 static int active_lock;
102 static int lock_fd; /* fd of LOCKFILE. */
103 static char *pkgadm_dir;
104
105 int pkgWlock(int verbose);
106 static int pkgWunlock(void);
107
108 /* forward declarations */
109
110 int relslock(void);
111
112 /*ARGSUSED*/
113 static void
do_alarm(int n)114 do_alarm(int n)
115 {
116 (void) signal(SIGALRM, SIG_IGN);
117 (void) signal(SIGALRM, do_alarm);
118 (void) alarm(LOCKWAIT);
119 }
120
121 /*
122 * Point packaging to the appropriate contents file. This is primarily used
123 * to establish a dryrun contents file. If the malloc() doesn't work, this
124 * returns 99 (internal error), else 0.
125 */
126 int
set_cfdir(char * cfdir)127 set_cfdir(char *cfdir)
128 {
129 char realcf[PATH_MAX];
130 char tmpcf[PATH_MAX];
131 int status;
132
133 if (cfdir == NULL) {
134 pkgadm_dir = get_PKGADM();
135 return (0);
136 }
137
138 if ((pkgadm_dir = strdup(cfdir)) == NULL) {
139 return (99);
140 }
141
142 (void) snprintf(tmpcf, sizeof (tmpcf), "%s/contents", pkgadm_dir);
143
144 /*
145 * return if a temporary contents file already exists -
146 * assume it is from a prior package in this series.
147 */
148
149 if (access(tmpcf, F_OK) == 0) {
150 return (0);
151 }
152
153 /*
154 * no temporary contents file exists - create one.
155 */
156
157 (void) snprintf(realcf, sizeof (realcf), "%s/contents", get_PKGADM());
158
159 /*
160 * If there's a contents file there already, copy it
161 * over, otherwise initialize one. Make sure that the
162 * server, if running, flushes the contents file.
163 */
164
165 (void) pkgsync(NULL, get_PKGADM(), B_FALSE);
166
167 /* create new contents file if one does not already exist */
168
169 if (access(realcf, F_OK) != 0) {
170 int n;
171
172 n = open(tmpcf, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
173 if (n < 0) {
174 progerr(gettext(ERR_CREAT_CONT), tmpcf,
175 strerror(errno));
176 return (99);
177 }
178 (void) close(n);
179 } else {
180
181 /* contents file exists, save in pkgadm-dir */
182
183 status = copyf(realcf, tmpcf, (time_t)0);
184 if (status != 0) {
185 progerr(gettext(ERR_DRCONTCP), tmpcf);
186 return (99);
187 }
188 }
189
190 return (0);
191 }
192
193 /*
194 * This function installs the database lock, opens the contents file for
195 * reading and creates and opens the temporary contents file for read/write.
196 * It returns 1 if successful, 0 otherwise.
197 */
198 int
ocfile(PKGserver * server,VFP_T ** r_tmpvfp,fsblkcnt_t map_blks)199 ocfile(PKGserver *server, VFP_T **r_tmpvfp, fsblkcnt_t map_blks)
200 {
201 struct stat64 statb, statl;
202 struct statvfs64 svfsb;
203 fsblkcnt_t free_blocks;
204 fsblkcnt_t need_blocks;
205 fsblkcnt_t log_blocks;
206 VFP_T *tmpvfp = (VFP_T *)NULL;
207 char contents[PATH_MAX];
208 char logfile[PATH_MAX];
209 int n;
210 off_t cdiff_alloc;
211 PKGserver newserver;
212
213 /* establish package administration contents directory location */
214
215 if (pkgadm_dir == NULL) {
216 if (set_cfdir(NULL) != 0) {
217 progerr(gettext(ERR_CFDIR));
218 return (0);
219 }
220 }
221
222 /* Lock the file for exclusive access */
223
224 if (!pkgWlock(1)) {
225 progerr(gettext(ERR_NOLOCK));
226 return (0);
227 }
228
229 if (*server != NULL) {
230 vfpTruncate(*r_tmpvfp);
231 (void) vfpClearModified(*r_tmpvfp);
232
233 return (1);
234 }
235
236 newserver = pkgopenserver(NULL, pkgadm_dir, B_FALSE);
237
238 /* The error has been reported. */
239 if (newserver == NULL)
240 return (0);
241
242 /* reset return VFP/FILE pointers */
243
244 (*r_tmpvfp) = (VFP_T *)NULL;
245
246 /* determine path to the primary contents file */
247 (void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
248
249 /*
250 * Check and see if there is enough space for the packaging commands
251 * to back up the contents file, if there is not, then do not allow
252 * execution to continue by failing the ocfile() call.
253 */
254
255 /* Get the contents file size */
256
257 if (stat64(contents, &statb) == -1) {
258 int lerrno = errno;
259
260 progerr(gettext(ERR_NOCFILE), contents);
261 logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
262 pkgcloseserver(newserver);
263 return (0);
264 }
265
266 /* Get the filesystem space */
267
268 if (statvfs64(contents, &svfsb) == -1) {
269 int lerrno = errno;
270
271 progerr(gettext(ERR_NOSTATV), contents);
272 logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
273 pkgcloseserver(newserver);
274 return (0);
275 }
276
277 free_blocks = (((fsblkcnt_t)svfsb.f_frsize > 0) ?
278 howmany(svfsb.f_frsize, DEV_BSIZE) :
279 howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
280
281 /* determine blocks used by the logfile */
282 (void) snprintf(logfile, sizeof (logfile), "%s/" PKGLOG, pkgadm_dir);
283
284 if (stat64(logfile, &statl) == -1)
285 log_blocks = 0;
286 else
287 log_blocks = nblk(statl.st_size, svfsb.f_bsize, svfsb.f_frsize);
288
289 /*
290 * Calculate the number of blocks we need to be able to operate on
291 * the contents file and the log file.
292 * When adding a package (map_blks > 0), we add the size of the
293 * pkgmap file times 1.5 as the pkgmap is a bit smaller then the
294 * lines added to the contents file. That data is written both to
295 * the new contents file and the log file (2 * 1.5 * map_blks).
296 * The new contents file is limited by the size of the current
297 * contents file and the increased log file.
298 * If we're removing a package, then the log might grow to the size
299 * of the full contents file but then the new contents file would
300 * be zero and so we only need to add the size of the contents file.
301 */
302 need_blocks = map_blks * 3 +
303 /* Current log file */
304 log_blocks +
305 /* Current contents file */
306 nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
307
308 if ((need_blocks + 10) > free_blocks) {
309 progerr(gettext(ERR_CFBACK), contents);
310 progerr(gettext(ERR_CFBACK1), need_blocks, free_blocks,
311 DEV_BSIZE);
312 pkgcloseserver(newserver);
313 return (0);
314 }
315
316 /*
317 * open the temporary contents file without a path name - this causes
318 * the "vfp" to be opened on in-memory storage only, the size of which
319 * is set following a successful return - this causes the temporary
320 * contents file to be maintained in memory only - if no changes are
321 * made as the primary contents file is processed, the in memory data
322 * is discarded and not written to the disk.
323 */
324
325 if (vfpOpen(&tmpvfp, (char *)NULL, "w", VFP_NONE) != 0) {
326 int lerrno = errno;
327
328 progerr(gettext(ERR_NOTMPOPEN));
329 logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
330 pkgcloseserver(newserver);
331 return (0);
332 }
333
334 /*
335 * set size of allocation for temporary contents file - this sets the
336 * size of the in-memory buffer associated with the open vfp.
337 * We only store the new and changed entries.
338 * We allocate memory depending on the size of the pkgmap; it's not
339 * completely right but <some value + * 1.5 * map_blks * DEV_BSIZE>
340 * seems fine (an install adds the size if the name of the package.)
341 */
342
343 cdiff_alloc = map_blks * DEV_BSIZE;
344 cdiff_alloc += cdiff_alloc/2;
345 if (cdiff_alloc < 1000000)
346 cdiff_alloc += 1000000;
347
348 if (vfpSetSize(tmpvfp, cdiff_alloc) != 0) {
349 int lerrno = errno;
350
351 progerr(gettext(ERR_NOTMPOPEN));
352 logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
353 (void) vfpClose(&tmpvfp);
354 pkgcloseserver(newserver);
355 return (0);
356 }
357
358 /* set return ->s to open server/vfps */
359
360 (*r_tmpvfp) = tmpvfp;
361 *server = newserver;
362
363 return (1); /* All OK */
364 }
365
366 /*
367 * This is a simple open and lock of the contents file. It doesn't create a
368 * temporary contents file and it doesn't need to do any space checking.
369 * Returns 1 for OK and 0 for "didn't do it".
370 */
371 int
socfile(PKGserver * server,boolean_t quiet)372 socfile(PKGserver *server, boolean_t quiet)
373 {
374 char contents[PATH_MAX];
375 boolean_t readonly = B_FALSE;
376 PKGserver newserver;
377
378 if (pkgadm_dir == NULL) {
379 if (set_cfdir(NULL) != 0) {
380 progerr(gettext(ERR_CFDIR));
381 return (0);
382 }
383 }
384
385 /*
386 * Lock the database for exclusive access, but don't make a fuss if
387 * it fails (user may not be root and the .pkg.lock file may not
388 * exist yet).
389 */
390
391 if (!pkgWlock(0)) {
392 if (!quiet)
393 logerr(gettext(MSG_NOLOCK));
394 readonly = B_TRUE;
395 }
396
397 newserver = pkgopenserver(NULL, pkgadm_dir, readonly);
398 if (newserver == NULL)
399 return (0);
400
401 *server = newserver;
402 return (1);
403 }
404
405 /*
406 * Name: swapcfile
407 * Description: This function closes both the current and temporary contents
408 * files specified, and conditionally replaces the old transitory
409 * contents file with the newly updated temporary contents file.
410 * The "ocfile()" or "socfile()" functions must be called to re-
411 * open the real contents file for processing.
412 * Arguments: PKGserver - handle to the package database
413 * a_cfTmpVfp - (VFP_T **) - [RW, *RW]
414 * This is the VFP associated which contains all the
415 * modifications to be written back to the database.
416 * file that is being written to.
417 * pkginst - (char) - [RO, *RO]
418 * This is the name of the package being operated on;
419 * this is used to write the "last modified by xxx"
420 * comment at the end of the contents file.
421 * dbchg - (int) - [RO]
422 * == 0 - the temporary contents file has NOT been changed
423 * with respect to the real contents file; do not
424 * update the real contents file with the contents
425 * of the temporary contents file.
426 * != 0 - the temporary contetns file HAS been changed with
427 * respect to the real contents file; DO update the
428 * real contents file with the contents of the
429 * temporary contents file.
430 * Returns: int == RESULT_OK - successful
431 * == RESULT_WRN - successful with warnings
432 * == RESULT_ERR - failed with fatal errors - deserves an
433 * alarming message and a quit()
434 * NOTES: If dbchg != 0, the contents file is always updated. If dbchg == 0,
435 * the contents file is updated IF the data is modified indication
436 * is set on the contents file associated with a_cfTmpVfp.
437 */
438
439 int
swapcfile(PKGserver server,VFP_T ** a_cfTmpVfp,char * pkginst,int dbchg)440 swapcfile(PKGserver server, VFP_T **a_cfTmpVfp, char *pkginst, int dbchg)
441 {
442 char *pe;
443 char *pl;
444 char *ps;
445 char line[256];
446 char timeb[BUFSIZ];
447 int retval = RESULT_OK;
448 struct tm *timep;
449 time_t clock;
450
451 /* normalize pkginst so its never null */
452
453 if (pkginst == (char *)NULL) {
454 dbchg = 0;
455 pkginst = "<unknown>";
456 }
457
458 /*
459 * If no changes were made to the database, checkpoint the temporary
460 * contents file - if this fails, then just close the file which causes
461 * the contents file to be reopened and reread if it is needed again
462 */
463
464 if ((dbchg == 0) && (vfpGetModified(*a_cfTmpVfp) == 0)) {
465 (void) pkgWunlock(); /* Free the database lock. */
466 return (retval);
467 }
468
469 /*
470 * changes made to the current temporary contents file -
471 * remove any trailing comment lines in the temp contents file, then
472 * append updated modification info records to temp contents file
473 */
474
475 pe = vfpGetCurrCharPtr(*a_cfTmpVfp); /* last char in contents file */
476 ps = vfpGetFirstCharPtr(*a_cfTmpVfp); /* 1st char in contents file */
477 pl = pe; /* last match is last char in contents file */
478
479 /* skip past all trailing newlines and null bytes */
480
481 while ((pe > ps) && ((*pe == '\n') || (*pe == '\0'))) {
482 pe--;
483 }
484
485 /* remove trailing comments as long as there are lines in the file */
486
487 while (pe > ps) {
488 if (*pe != '\n') {
489 /* curr char is not newline: backup one byte */
490 pl = pe--;
491 } else if (*pl != '#') {
492 /* curr char is newline next char not comment break */
493 break;
494 } else {
495 /* curr char is newline next char is comment - remove */
496 *pl = '\0';
497 vfpSetLastCharPtr(*a_cfTmpVfp, pl);
498 pe--;
499 }
500 }
501
502 /* create two update comment lines */
503
504 (void) time(&clock);
505 timep = localtime(&clock);
506
507 (void) strftime(timeb, sizeof (timeb), "%c\n", timep);
508 (void) snprintf(line, sizeof (line),
509 gettext("# Last modified by %s for %s package\n# %s"),
510 get_prog_name(), pkginst, timeb);
511 vfpPuts(*a_cfTmpVfp, line);
512
513 /* commit temporary contents file bytes to storage */
514
515 if (pkgservercommitfile(*a_cfTmpVfp, server) != 0) {
516 int lerrno = errno;
517
518 logerr(gettext(ERR_COMMIT));
519 vfpClose(a_cfTmpVfp);
520 pkgcloseserver(server);
521 (void) pkgWunlock(); /* Free the database lock. */
522 return (RESULT_ERR);
523 }
524
525 return (relslock() == 0 ? RESULT_ERR : retval);
526 }
527
528 /* This function releases the lock on the package database. */
529 int
relslock(void)530 relslock(void)
531 {
532 /*
533 * This closes the contents file and releases the lock.
534 */
535 if (!pkgWunlock()) {
536 int lerrno = errno;
537
538 progerr(gettext(ERR_NOUPD));
539 logerr(gettext(ERR_FCLOSE_FAIL), lerrno);
540 return (0);
541 }
542 return (1);
543 }
544
545 /*
546 * This function attempts to lock the package database. It returns 1 on
547 * success, 0 on failure. The positive logic verbose flag determines whether
548 * or not the function displays the error message upon failure.
549 */
550 int
pkgWlock(int verbose)551 pkgWlock(int verbose) {
552 int retry_cnt, retval;
553 char lockpath[PATH_MAX];
554
555 active_lock = 0;
556
557 (void) snprintf(lockpath, sizeof (lockpath),
558 "%s/%s", pkgadm_dir, LOCKFILE);
559
560 retry_cnt = LOCKRETRY;
561
562 /*
563 * If the lock file is not present, create it. The mode is set to
564 * allow any process to lock the database, that's because pkgchk may
565 * be run by a non-root user.
566 */
567 if (access(lockpath, F_OK) == -1) {
568 lock_fd = open(lockpath, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0644);
569 if (lock_fd < 0) {
570 if (verbose)
571 progerr(gettext(ERR_MKLOCK), lockpath);
572 return (0);
573 } else {
574 (void) fchmod(lock_fd, 0644); /* force perms. */
575 }
576 } else {
577 if ((lock_fd = open(lockpath, O_RDWR)) == -1) {
578 if (verbose)
579 progerr(gettext(ERR_OPLOCK), lockpath);
580 return (0);
581 }
582 }
583
584 (void) signal(SIGALRM, do_alarm);
585 (void) alarm(LOCKWAIT);
586
587 do {
588 if (lockf(lock_fd, F_LOCK, 0)) {
589 if (errno == EAGAIN || errno == EINTR)
590 logerr(gettext(MSG_XWTING));
591 else if (errno == ECOMM) {
592 logerr(gettext(ERR_LCKREM));
593 retval = 0;
594 break;
595 } else if (errno == EBADF) {
596 logerr(gettext(ERR_BADLCK));
597 retval = 0;
598 break;
599 } else if (errno == EDEADLK) {
600 logerr(gettext(ERR_DEADLCK));
601 retval = 0;
602 break;
603 }
604 } else {
605 active_lock = 1;
606 retval = 1;
607 break;
608 }
609 } while (retry_cnt--);
610
611 (void) signal(SIGALRM, SIG_IGN);
612
613 if (retval == 0)
614 {
615 if (retry_cnt == -1) {
616 logerr(gettext(ERR_TMOUT));
617 }
618
619 (void) pkgWunlock(); /* close the lockfile. */
620 }
621
622 return (retval);
623 }
624
625 /*
626 * Release the lock on the package database. Returns 1 on success, 0 on
627 * failure.
628 */
629 static int
pkgWunlock(void)630 pkgWunlock(void) {
631 if (active_lock) {
632 active_lock = 0;
633 if (close(lock_fd))
634 return (0);
635 else
636 return (1);
637 } else
638 return (1);
639 }
640
641 /*
642 * This function verifies that the contents file is in place.
643 * returns 1 - if it exists
644 * returns 0 - if it does not exist
645 */
646 int
iscfile(void)647 iscfile(void)
648 {
649 char contents[PATH_MAX];
650
651 (void) snprintf(contents, PATH_MAX, "%s/contents", get_PKGADM());
652
653 return (access(contents, F_OK) == 0 ? 1 : 0);
654 }
655
656 /*
657 * This function verifies that the contents file is in place. If it is - no
658 * change. If it isn't - this creates it.
659 * Returns: == 0 : failure
660 * != 0 : success
661 */
662
663 int
vcfile(void)664 vcfile(void)
665 {
666 int lerrno;
667 int fd;
668 char contents[PATH_MAX];
669
670 /*
671 * create full path to contents file
672 */
673
674 (void) snprintf(contents, sizeof (contents),
675 "%s/contents", get_PKGADM());
676
677 /*
678 * Attempt to create the file - will only be successful
679 * if the file does not currently exist.
680 */
681
682 fd = open(contents, O_WRONLY | O_CREAT | O_EXCL, 0644);
683 if (fd >= 0) {
684 /*
685 * Contents file wasn't there, but is now.
686 */
687
688 echo(gettext("## Software contents file initialized"));
689 (void) close(fd);
690 return (1); /* success */
691 }
692
693 /*
694 * Could not create the file - it may exist or there may be
695 * permissions issues - find out and act accordingly.
696 */
697
698 lerrno = errno;
699
700 /* success if error is 'file exists' */
701
702 if (lerrno == EEXIST) {
703 return (1); /* success */
704 }
705
706 /* success if error is 'permission denied' but file exists */
707
708 if (lerrno == EACCES) {
709 /*
710 * Because O_CREAT and O_EXCL are specified in open(),
711 * if the contents file already exists, the open will
712 * fail with EACCES - determine if this is the case -
713 * if so return success.
714 */
715
716 if (access(contents, F_OK) == 0) {
717 return (1); /* success */
718 }
719
720 /*
721 * access() failed - if because of permissions failure this
722 * means the contents file exists but it cannot be accessed
723 * or the path to the contents file cannot be accessed - in
724 * either case the contents file cannot be accessed.
725 */
726
727 if (errno == EACCES) {
728 progerr(gettext(ERR_ACCESS_CONT), contents,
729 strerror(lerrno));
730 logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
731 return (0); /* failure */
732 }
733 }
734
735 /*
736 * the contents file does not exist and it cannot be created.
737 */
738
739 progerr(gettext(ERR_CREAT_CONT), contents, strerror(lerrno));
740 logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
741 return (0); /* failure */
742 }
743