patch-2.4.20 linux-2.4.20/fs/intermezzo/upcall.c

Next file: linux-2.4.20/fs/intermezzo/vfs.c
Previous file: linux-2.4.20/fs/intermezzo/sysctl.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/fs/intermezzo/upcall.c linux-2.4.20/fs/intermezzo/upcall.c
@@ -1,22 +1,28 @@
-/*
- * Mostly platform independent upcall operations to Venus:
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001, 2002 Cluster File Systems, Inc. <braam@clusterfs.com>
+ * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
+ *
+ *   This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ *   InterMezzo is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   InterMezzo is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with InterMezzo; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Mostly platform independent upcall operations to a cache manager:
  *  -- upcalls
  *  -- upcall routines
  *
- * Linux 2.0 version
- * Copyright (C) 1996 Peter J. Braam <braam@cs.cmu.edu>,
- * Michael Callahan <callahan@maths.ox.ac.uk>
- *
- * Redone for Linux 2.1
- * Copyright (C) 1997 Carnegie Mellon University
- *
- * Carnegie Mellon University encourages users of this code to contribute
- * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
- *
- * Much cleaned up for InterMezzo
- * Copyright (C) 1998 Peter J. Braam <braam@cs.cmu.edu>,
- * Copyright (C) 1999 Carnegie Mellon University
- *
  */
 
 #include <asm/system.h>
@@ -39,210 +45,513 @@
 #include <linux/vmalloc.h>
 #include <asm/segment.h>
 
+#include <linux/intermezzo_lib.h>
 #include <linux/intermezzo_fs.h>
-#include <linux/intermezzo_upcall.h>
 #include <linux/intermezzo_psdev.h>
 
+#include <linux/intermezzo_idl.h>
+
 /*
-  At present: four upcalls
-  - opendir: fetch a directory (synchronous & asynchronous)
-  - open: fetch file (synchronous)
-  - journal: send a journal page (asynchronous)
-  - permit: get a permit (synchronous)
+  At present:
+  -- Asynchronous calls:
+   - kml:            give a "more" kml indication to userland
+   - kml_truncate:   initiate KML truncation
+   - release_permit: kernel is done with permit
+  -- Synchronous
+   - open:           fetch file
+   - permit:         get a permit
 
-  Errors returned here are positive.
+  Errors returned by user level code are positive
 
  */
 
+static struct izo_upcall_hdr *upc_pack(__u32 opcode, int pathlen, char *path,
+                                       char *fsetname, int reclen, char *rec,
+                                       int *size)
+{
+        struct izo_upcall_hdr *hdr;
+        char *ptr;
+        ENTRY;
 
-#define INSIZE(tag) sizeof(struct lento_ ## tag ## _in)
-#define OUTSIZE(tag) sizeof(struct lento_ ## tag ## _out)
-#define SIZE(tag)  ( (INSIZE(tag)>OUTSIZE(tag)) ? INSIZE(tag) : OUTSIZE(tag) )
-
-#define UPARG(op)\
-do {\
-        PRESTO_ALLOC(inp, union up_args *, insize);\
-        if ( !inp ) { return -ENOMEM; }\
-        outp = (union down_args *) (inp);\
-        inp->uh.opcode = (op);\
-        inp->uh.pid = current->pid;\
-        inp->uh.uid = current->fsuid;\
-        outsize = insize;\
-} while (0)
-
-#define BUFF_ALLOC(buffer)                              \
-        PRESTO_ALLOC(buffer, char *, PAGE_SIZE);        \
-        if ( !buffer ) {                                \
-                printk("PRESTO: out of memory!\n");     \
-                return -ENOMEM;                         \
+        *size = sizeof(struct izo_upcall_hdr);
+        if ( fsetname ) {
+                *size += round_strlen(fsetname);
+        }
+        if ( path ) { 
+                *size += round_strlen(path);
         }
+        if ( rec ) { 
+                *size += size_round(reclen);
+        }
+        PRESTO_ALLOC(hdr, *size);
+        if (!hdr) { 
+                CERROR("intermezzo upcall: out of memory (opc %d)\n", opcode);
+                EXIT;
+                return NULL;
+        }
+        memset(hdr, 0, *size);
+
+        ptr = (char *)hdr + sizeof(*hdr);
+
+        /* XXX do we need fsuid ? */
+        hdr->u_len = *size;
+        hdr->u_version = IZO_UPC_VERSION;
+        hdr->u_opc = opcode;
+        hdr->u_pid = current->pid;
+        hdr->u_uid = current->fsuid;
+
+        if (path) { 
+                /*XXX Robert: please review what len to pass in for 
+                  NUL terminated strings */
+                hdr->u_pathlen = strlen(path);
+                LOGL0(path, hdr->u_pathlen, ptr);
+        }
+        if (fsetname) { 
+                hdr->u_fsetlen = strlen(fsetname);
+                LOGL0(fsetname, strlen(fsetname), ptr);
+        }
+        if (rec) { 
+                hdr->u_reclen = reclen;
+                LOGL(rec, reclen, ptr);
+        }
+        
+        EXIT;
+        return hdr;
+}
 
 /* the upcalls */
-int lento_kml(int minor, unsigned int offset, unsigned int first_recno,
-              unsigned int length, unsigned int last_recno, int namelen,
-              char *fsetname)
-{
-        union up_args *inp;
-        union down_args *outp;
-        int insize, outsize, error;
+int izo_upc_kml(int minor, __u64 offset, __u32 first_recno, __u64 length, __u32 last_recno, char *fsetname)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+
         ENTRY;
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return 0;
+        }
+
+        hdr = upc_pack(IZO_UPC_KML, 0, NULL, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        hdr->u_offset = offset;
+        hdr->u_first_recno = first_recno;
+        hdr->u_length = length;
+        hdr->u_last_recno = last_recno;
+
+        CDEBUG(D_UPCALL, "KML: fileset %s, offset %Lu, length %Lu, "
+               "first %u, last %d; minor %d\n",
+               fsetname, hdr->u_offset, hdr->u_length, hdr->u_first_recno,
+               hdr->u_last_recno, minor);
+
+        error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
+
+        EXIT;
+        return -error;
+}
+
+int izo_upc_kml_truncate(int minor, __u64 length, __u32 last_recno, char *fsetname)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
 
+        ENTRY;
         if (!presto_lento_up(minor)) {
                 EXIT;
                 return 0;
         }
 
-        insize = SIZE(kml) + namelen + 1;
-        UPARG(LENTO_KML);
-        inp->lento_kml.namelen = namelen;
-        memcpy(inp->lento_kml.fsetname, fsetname, namelen);
-        inp->lento_kml.fsetname[namelen] = '\0';
-        inp->lento_kml.offset = offset;
-        inp->lento_kml.first_recno = first_recno;
-        inp->lento_kml.length = length;
-        inp->lento_kml.last_recno = last_recno;
-
-        CDEBUG(D_UPCALL, "KML: fileset %s, offset %d, length %d, "
-               "first %d, last %d; minor %d\n",
-               inp->lento_kml.fsetname,
-               inp->lento_kml.offset,
-               inp->lento_kml.length,
-               inp->lento_kml.first_recno,
-               inp->lento_kml.last_recno, minor);
+        hdr = upc_pack(IZO_UPC_KML_TRUNC, 0, NULL, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        hdr->u_length = length;
+        hdr->u_last_recno = last_recno;
+
+        CDEBUG(D_UPCALL, "KML TRUNCATE: fileset %s, length %Lu, "
+               "last recno %d, minor %d\n",
+               fsetname, hdr->u_length, hdr->u_last_recno, minor);
 
-        error = lento_upcall(minor, insize, &outsize, inp,
-                             ASYNCHRONOUS, NULL);
+        error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
 
         EXIT;
         return error;
 }
 
-int lento_release_permit( int minor, int mycookie )
+int izo_upc_open(int minor, __u32 pathlen, char *path, char *fsetname, struct lento_vfs_context *info)
 {
-        union up_args *inp;
-        union down_args *outp;
-        int insize, outsize, error;
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
         ENTRY;
 
         if (!presto_lento_up(minor)) {
                 EXIT;
-                return 0;
+                return -EIO;
         }
 
-        insize= SIZE(response_cookie);
-        UPARG(LENTO_COOKIE);
-        inp->lento_response_cookie.cookie= mycookie;
+        hdr = upc_pack(IZO_UPC_OPEN, pathlen, path, fsetname, 
+                       sizeof(*info), (char*)info, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
 
-        CDEBUG(D_UPCALL, "cookie %d\n", mycookie);
+        CDEBUG(D_UPCALL, "path %s\n", path);
 
-        error = lento_upcall(minor, insize, &outsize, inp,
-                             ASYNCHRONOUS, NULL);
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
 
         EXIT;
-        return error;
+        return -error;
 }
 
-int lento_opendir(int minor, int pathlen, char *path, int async)
+int izo_upc_get_fileid(int minor, __u32 reclen, char *rec, 
+                       __u32 pathlen, char *path, char *fsetname)
 {
-        union up_args *inp;
-        union down_args *outp;
-        int insize, outsize, error;
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
         ENTRY;
 
-        insize = SIZE(opendir) + pathlen + 1;
-        UPARG(LENTO_OPENDIR);
-        inp->lento_opendir.async = async;
-        inp->lento_opendir.pathlen = pathlen;
-        memcpy(inp->lento_opendir.path, path, pathlen);
-        inp->lento_opendir.path[pathlen] = '\0';
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
+
+        hdr = upc_pack(IZO_UPC_GET_FILEID, pathlen, path, fsetname, reclen, rec, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
 
-        CDEBUG(D_UPCALL, "path %s\n", inp->lento_opendir.path);
+        CDEBUG(D_UPCALL, "path %s\n", path);
 
-        if (async) {
-                error = lento_upcall(minor, insize, &outsize, inp,
-                                     ASYNCHRONOUS, NULL);
-                return 0;
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+        EXIT;
+        return -error;
+}
+
+int izo_upc_backfetch(int minor, char *path, char *fsetname, struct lento_vfs_context *info)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
+
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
         }
 
-        error = lento_upcall(minor, insize, &outsize, inp,
-                             SYNCHRONOUS, NULL);
-        if (error && error != EISFSETROOT) {
-                printk("lento_opendir: error %d\n", error);
+        hdr = upc_pack(IZO_UPC_BACKFETCH, strlen(path), path, fsetname, 
+                       sizeof(*info), (char *)info, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
         }
 
+        /* This is currently synchronous, kml_reint_record blocks */
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
         EXIT;
-        return error;
+        return -error;
 }
 
-int lento_open(int minor, int pathlen, char *path)
+int izo_upc_permit(int minor, struct dentry *dentry, __u32 pathlen, char *path,
+                   char *fsetname)
 {
-        union up_args *inp;
-        union down_args *outp;
-        int insize, outsize, error;
-
-	ENTRY;
-	insize = SIZE(open) + pathlen + 1;
-	UPARG(LENTO_OPEN);
-	inp->lento_open.pathlen = pathlen;
-	memcpy(inp->lento_open.path, path, pathlen);
-	inp->lento_open.path[pathlen] = '\0';
-
-	CDEBUG(D_UPCALL, "path %s\n", inp->lento_open.path);
-
-	error = lento_upcall(minor, insize, &outsize, inp,
-			     SYNCHRONOUS, NULL);
-	if (error) {
-	        printk("lento_open: error %d\n", error);
-	}
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+
+        ENTRY;
+
+        hdr = upc_pack(IZO_UPC_PERMIT, pathlen, path, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        CDEBUG(D_UPCALL, "Permit minor %d path %s\n", minor, path);
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+
+        if (error == -EROFS) {
+                int err;
+                CERROR("InterMezzo: ERROR - requested permit for read-only "
+                       "fileset.\n   Setting \"%s\" read-only!\n", path);
+                err = izo_mark_cache(dentry, 0xFFFFFFFF, CACHE_CLIENT_RO, NULL);
+                if (err)
+                        CERROR("InterMezzo ERROR: mark_cache %d\n", err);
+        } else if (error) {
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+        }
 
         EXIT;
         return error;
 }
 
+/* This is a ping-pong upcall handled on the server when a client (uuid)
+ * requests the permit for itself. */
+int izo_upc_revoke_permit(int minor, char *fsetname, __u8 uuid[16])
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+
+        ENTRY;
+
+        hdr = upc_pack(IZO_UPC_REVOKE_PERMIT, 0, NULL, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
 
-int lento_permit(int minor, int pathlen, int fsetnamelen, char *path, char *fsetname)
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+
+        if (error)
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+        EXIT;
+        return -error;
+}
+
+int izo_upc_go_fetch_kml(int minor, char *fsetname, __u8 uuid[16],
+                         __u64 kmlsize)
 {
-        union up_args *inp;
-        union down_args *outp;
-        int insize, outsize, error;
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
         ENTRY;
 
-        insize = SIZE(permit) + pathlen + 1 + fsetnamelen + 1;
-        UPARG(LENTO_PERMIT);
-        inp->lento_permit.pathlen = pathlen;
-        inp->lento_permit.fsetnamelen = fsetnamelen;
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
+
+        hdr = upc_pack(IZO_UPC_GO_FETCH_KML, 0, NULL, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        hdr->u_offset = kmlsize;
+        memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+
+        error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
+        if (error)
+                CERROR("%s: error %d\n", __FUNCTION__, error);
 
-        memcpy(inp->lento_permit.path, path, pathlen);
-        inp->lento_permit.path[pathlen] = '\0';
+        EXIT;
+        return -error;
+}
 
-	memcpy(&(inp->lento_permit.path[pathlen+1]), fsetname, fsetnamelen); 
-        inp->lento_permit.path[fsetnamelen + 1 + pathlen] = '\0';
+int izo_upc_connect(int minor, __u64 ip_address, __u64 port, __u8 uuid[16],
+                    int client_flag)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
 
-        CDEBUG(D_UPCALL, "Permit minor %d path %s\n", minor,
-               inp->lento_permit.path);
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
 
-        error = lento_upcall(minor, insize, &outsize, inp,
-                             SYNCHRONOUS, NULL);
+        hdr = upc_pack(IZO_UPC_CONNECT, 0, NULL, NULL, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        hdr->u_offset = ip_address;
+        hdr->u_length = port;
+        memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+        hdr->u_first_recno = client_flag;
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
         if (error) {
-                if( error == -EROFS ) {
-                        int err;
-                        printk("lento_permit: ERROR - requested permit for "
-                               "read-only fileset.\n"
-                               "   Setting \"%s\" read-only!\n",
-                               path);
-                        err= presto_mark_cache(path, 0xFFFFFFFF, 
-                                               CACHE_CLIENT_RO, NULL);
-                        if( err ) {
-                                printk("ERROR : mark_cache %d\n", err);
-                        }
-                }
-                else {
-                        printk("lento_permit: error %d\n", error);
-                }
+                CERROR("%s: error %d\n", __FUNCTION__, error);
         }
 
         EXIT;
+        return -error;
+}
+
+int izo_upc_set_kmlsize(int minor, char *fsetname, __u8 uuid[16], __u64 kmlsize)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
+
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
+
+        hdr = upc_pack(IZO_UPC_SET_KMLSIZE, 0, NULL, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+        hdr->u_length = kmlsize;
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("%s: error %d\n", __FUNCTION__, error);
+
+        EXIT;
+        return -error;
+}
+
+int izo_upc_repstatus(int minor,  char * fsetname, struct izo_rcvd_rec *lr_server)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
+
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
+
+        hdr = upc_pack(IZO_UPC_REPSTATUS, 0, NULL, fsetname, 
+                       sizeof(*lr_server), (char*)lr_server, 
+                       &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("%s: error %d\n", __FUNCTION__, error);
+
+        EXIT;
+        return -error;
+}
+
+
+#if 0
+int izo_upc_client_make_branch(int minor, char *fsetname, char *tagname,
+                               char *branchname)
+{
+        int size, error;
+        struct izo_upcall_hdr *hdr;
+        int pathlen;
+        char *path;
+        ENTRY;
+
+        hdr = upc_pack(IZO_UPC_CLIENT_MAKE_BRANCH, strlen(tagname), tagname,
+                       fsetname, strlen(branchname) + 1, branchname, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                error = -PTR_ERR(hdr);
+                goto error;
+        }
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: error %d\n", error);
 
+ error:
+        PRESTO_FREE(path, pathlen);
+        EXIT;
         return error;
 }
+#endif
+
+int izo_upc_server_make_branch(int minor, char *fsetname)
+{
+        int size, error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
 
+        hdr = upc_pack(IZO_UPC_SERVER_MAKE_BRANCH, 0, NULL, fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                error = -PTR_ERR(hdr);
+                goto error;
+        }
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: error %d\n", error);
+
+ error:
+        EXIT;
+        return -error;
+}
+
+int izo_upc_branch_undo(int minor, char *fsetname, char *branchname)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
+
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
+
+        hdr = upc_pack(IZO_UPC_BRANCH_UNDO, strlen(branchname), branchname,
+                       fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+        EXIT;
+        return -error;
+}
+
+int izo_upc_branch_redo(int minor, char *fsetname, char *branchname)
+{
+        int size;
+        int error;
+        struct izo_upcall_hdr *hdr;
+        ENTRY;
+
+        if (!presto_lento_up(minor)) {
+                EXIT;
+                return -EIO;
+        }
+
+        hdr = upc_pack(IZO_UPC_BRANCH_REDO, strlen(branchname) + 1, branchname,
+                       fsetname, 0, NULL, &size);
+        if (!hdr || IS_ERR(hdr)) {
+                EXIT;
+                return -PTR_ERR(hdr);
+        }
+
+        error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+        if (error)
+                CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+        EXIT;
+        return -error;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)