From 9c2ccb43c3cc960334e2ab0069501dc583c7dbf7 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 17 Nov 2005 17:11:48 +0000 Subject: [PATCH] fix --- ChangeLog | 4 + include/fuse.h | 10 ++ include/fuse_lowlevel.h | 23 ++-- lib/fuse.c | 251 +++++++++++++++++++++++++--------------- lib/fuse_lowlevel.c | 93 ++++++++------- lib/helper.c | 59 +++++----- lib/mount_bsd.c | 14 ++- 7 files changed, 274 insertions(+), 180 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0c99a0d..51b6a19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2005-11-17 Miklos Szeredi + + * More FreeBSD merge + 2005-11-16 Miklos Szeredi * Merge library part of FreeBSD port. Patch by Csaba Henk diff --git a/include/fuse.h b/include/fuse.h index adf0c9a..4d58cd2 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -541,6 +541,8 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void)); * Compatibility stuff * * ----------------------------------------------------------- */ +#ifndef __FreeBSD__ + #if FUSE_USE_VERSION == 22 || FUSE_USE_VERSION == 21 || FUSE_USE_VERSION == 11 # include "fuse_compat.h" # undef FUSE_MINOR_VERSION @@ -583,6 +585,14 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void)); # error Compatibility with API version other than 21, 22 and 11 not supported #endif +#else /* __FreeBSD__ */ + +#if FUSE_USE_VERSION < 25 +# error On FreeBSD API version 25 or greater must be used +#endif + +#endif /* __FreeBSD__ */ + #ifdef __cplusplus } #endif diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index aa1e453..a88a898 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -1210,18 +1210,27 @@ void fuse_chan_destroy(struct fuse_chan *ch); * Compatibility stuff * * ----------------------------------------------------------- */ -#if FUSE_USE_VERSION == 24 -#include "fuse_lowlevel_compat.h" -#undef FUSE_MINOR_VERSION -#define FUSE_MINOR_VERSION 4 -#define fuse_file_info fuse_file_info_compat -#define fuse_reply_statfs fuse_reply_statfs_compat -#define fuse_reply_open fuse_reply_open_compat +#ifndef __FreeBSD__ +#if FUSE_USE_VERSION == 24 +# include "fuse_lowlevel_compat.h" +# undef FUSE_MINOR_VERSION +# define FUSE_MINOR_VERSION 4 +# define fuse_file_info fuse_file_info_compat +# define fuse_reply_statfs fuse_reply_statfs_compat +# define fuse_reply_open fuse_reply_open_compat #elif FUSE_USE_VERSION < 25 # error Compatibility with low level API version other than 24 not supported #endif +#else /* __FreeBSD__ */ + +#if FUSE_USE_VERSION < 25 +# error On FreeBSD API version 25 or greater must be used +#endif + +#endif /* __FreeBSD__ */ + #ifdef __cplusplus } #endif diff --git a/lib/fuse.c b/lib/fuse.c index 8966fab..976a26c 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -12,7 +12,6 @@ #include "fuse_i.h" #include "fuse_lowlevel.h" -#include "fuse_compat.h" #include #include @@ -113,6 +112,11 @@ struct fuse_dirhandle { static struct fuse_context *(*fuse_getcontext)(void) = NULL; +static int fuse_do_open(struct fuse *, char *, struct fuse_file_info *); +static void fuse_do_release(struct fuse *, char *, struct fuse_file_info *); +static int fuse_do_opendir(struct fuse *, char *, struct fuse_file_info *); +static int fuse_do_statfs(struct fuse *, char *, struct statvfs *); + #ifndef USE_UCLIBC #define mutex_init(mut) pthread_mutex_init(mut, NULL) #else @@ -1081,18 +1085,8 @@ static void fuse_open(fuse_req_t req, fuse_ino_t ino, if (f->op.open) { err = -ENOENT; path = get_path(f, ino); - if (path != NULL) { - if (!f->compat) - err = f->op.open(path, fi); - else if (f->compat == 22) { - struct fuse_file_info_compat22 tmp; - memcpy(&tmp, fi, sizeof(tmp)); - err = ((struct fuse_operations_compat22 *) &f->op)->open(path, &tmp); - memcpy(fi, &tmp, sizeof(tmp)); - fi->fh = tmp.fh; - } else - err = ((struct fuse_operations_compat2 *) &f->op)->open(path, fi->flags); - } + if (path != NULL) + err = fuse_do_open(f, path, fi); } if (!err) { if (f->flags & FUSE_DEBUG) { @@ -1109,12 +1103,8 @@ static void fuse_open(fuse_req_t req, fuse_ino_t ino, pthread_mutex_lock(&f->lock); if (fuse_reply_open(req, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ - if(f->op.release && path != NULL) { - if (!f->compat || f->compat >= 22) - f->op.release(path, fi); - else - ((struct fuse_operations_compat2 *) &f->op)->release(path, fi->flags); - } + if(f->op.release && path != NULL) + fuse_do_release(f, path, fi); } else { struct node *node = get_node(f, ino); node->open_count ++; @@ -1259,12 +1249,8 @@ static void fuse_release(fuse_req_t req, fuse_ino_t ino, fi->flags); fflush(stdout); } - if (f->op.release) { - if (!f->compat || f->compat >= 22) - f->op.release(path ? path : "-", fi); - else if (path) - ((struct fuse_operations_compat2 *) &f->op)->release(path, fi->flags); - } + if (f->op.release) + fuse_do_release(f, path, fi); if(unlink_hidden && path) f->op.unlink(path); @@ -1342,15 +1328,8 @@ static void fuse_opendir(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - if (!f->compat) { - err = f->op.opendir(path, &fi); - dh->fh = fi.fh; - } else { - struct fuse_file_info_compat22 tmp; - memcpy(&tmp, &fi, sizeof(tmp)); - err = ((struct fuse_operations_compat22 *) &f->op)->opendir(path, &tmp); - dh->fh = tmp.fh; - } + err = fuse_do_opendir(f, path, &fi); + dh->fh = fi.fh; } if (!err) { pthread_mutex_lock(&f->lock); @@ -1564,29 +1543,6 @@ static int default_statfs(struct statvfs *buf) return 0; } -static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf, - struct statvfs *stbuf) -{ - stbuf->f_bsize = compatbuf->block_size; - stbuf->f_blocks = compatbuf->blocks; - stbuf->f_bfree = compatbuf->blocks_free; - stbuf->f_bavail = compatbuf->blocks_free; - stbuf->f_files = compatbuf->files; - stbuf->f_ffree = compatbuf->files_free; - stbuf->f_namemax = compatbuf->namelen; -} - -static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf) -{ - stbuf->f_bsize = oldbuf->f_bsize; - stbuf->f_blocks = oldbuf->f_blocks; - stbuf->f_bfree = oldbuf->f_bfree; - stbuf->f_bavail = oldbuf->f_bavail; - stbuf->f_files = oldbuf->f_files; - stbuf->f_ffree = oldbuf->f_ffree; - stbuf->f_namemax = oldbuf->f_namelen; -} - static void fuse_statfs(fuse_req_t req) { struct fuse *f = req_fuse_prepare(req); @@ -1595,20 +1551,7 @@ static void fuse_statfs(fuse_req_t req) memset(&buf, 0, sizeof(buf)); if (f->op.statfs) { - if (!f->compat) { - err = f->op.statfs("/", &buf); - } else if (f->compat > 11) { - struct statfs oldbuf; - err = ((struct fuse_operations_compat22 *) &f->op)->statfs("/", &oldbuf); - if (!err) - convert_statfs_old(&oldbuf, &buf); - } else { - struct fuse_statfs_compat1 compatbuf; - memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1)); - err = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf); - if (!err) - convert_statfs_compat(&compatbuf, &buf); - } + err = fuse_do_statfs(f, "/", &buf); } else err = default_statfs(&buf); @@ -2060,31 +2003,6 @@ struct fuse *fuse_new(int fd, const char *opts, return fuse_new_common(fd, opts, op, op_size, 0); } -struct fuse *fuse_new_compat22(int fd, const char *opts, - const struct fuse_operations_compat22 *op, - size_t op_size) -{ - return fuse_new_common(fd, opts, (struct fuse_operations *) op, - op_size, 22); -} - -struct fuse *fuse_new_compat2(int fd, const char *opts, - const struct fuse_operations_compat2 *op) -{ - return fuse_new_common(fd, opts, (struct fuse_operations *) op, - sizeof(struct fuse_operations_compat2), 21); -} - -struct fuse *fuse_new_compat1(int fd, int flags, - const struct fuse_operations_compat1 *op) -{ - const char *opts = NULL; - if (flags & FUSE_DEBUG_COMPAT1) - opts = "debug"; - return fuse_new_common(fd, opts, (struct fuse_operations *) op, - sizeof(struct fuse_operations_compat1), 11); -} - void fuse_destroy(struct fuse *f) { size_t i; @@ -2117,9 +2035,150 @@ void fuse_destroy(struct fuse *f) free(f); } +#ifndef __FreeBSD__ + +#include "fuse_compat.h" + +static int fuse_do_open(struct fuse *f, char *path, struct fuse_file_info *fi) +{ + if (!f->compat) + return f->op.open(path, fi); + else if (f->compat == 22) { + int err; + struct fuse_file_info_compat22 tmp; + memcpy(&tmp, fi, sizeof(tmp)); + err = ((struct fuse_operations_compat22 *) &f->op)->open(path, &tmp); + memcpy(fi, &tmp, sizeof(tmp)); + fi->fh = tmp.fh; + return err; + } else + return + ((struct fuse_operations_compat2 *) &f->op)->open(path, fi->flags); +} + +static void fuse_do_release(struct fuse *f, char *path, + struct fuse_file_info *fi) +{ + if (!f->compat || f->compat >= 22) + f->op.release(path ? path : "-", fi); + else if (path) + ((struct fuse_operations_compat2 *) &f->op)->release(path, fi->flags); +} + +static int fuse_do_opendir(struct fuse *f, char *path, + struct fuse_file_info *fi) +{ + if (!f->compat) { + return f->op.opendir(path, fi); + } else { + int err; + struct fuse_file_info_compat22 tmp; + memcpy(&tmp, fi, sizeof(tmp)); + err = ((struct fuse_operations_compat22 *) &f->op)->opendir(path, &tmp); + memcpy(fi, &tmp, sizeof(tmp)); + fi->fh = tmp.fh; + return err; + } +} + +static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf, + struct statvfs *stbuf) +{ + stbuf->f_bsize = compatbuf->block_size; + stbuf->f_blocks = compatbuf->blocks; + stbuf->f_bfree = compatbuf->blocks_free; + stbuf->f_bavail = compatbuf->blocks_free; + stbuf->f_files = compatbuf->files; + stbuf->f_ffree = compatbuf->files_free; + stbuf->f_namemax = compatbuf->namelen; +} + +static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf) +{ + stbuf->f_bsize = oldbuf->f_bsize; + stbuf->f_blocks = oldbuf->f_blocks; + stbuf->f_bfree = oldbuf->f_bfree; + stbuf->f_bavail = oldbuf->f_bavail; + stbuf->f_files = oldbuf->f_files; + stbuf->f_ffree = oldbuf->f_ffree; + stbuf->f_namemax = oldbuf->f_namelen; +} + +static int fuse_do_statfs(struct fuse *f, char *path, struct statvfs *buf) +{ + int err; + + if (!f->compat) { + err = f->op.statfs(path, buf); + } else if (f->compat > 11) { + struct statfs oldbuf; + err = ((struct fuse_operations_compat22 *) &f->op)->statfs("/", &oldbuf); + if (!err) + convert_statfs_old(&oldbuf, buf); + } else { + struct fuse_statfs_compat1 compatbuf; + memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1)); + err = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf); + if (!err) + convert_statfs_compat(&compatbuf, buf); + } + return err; +} + +struct fuse *fuse_new_compat22(int fd, const char *opts, + const struct fuse_operations_compat22 *op, + size_t op_size) +{ + return fuse_new_common(fd, opts, (struct fuse_operations *) op, + op_size, 22); +} + +struct fuse *fuse_new_compat2(int fd, const char *opts, + const struct fuse_operations_compat2 *op) +{ + return fuse_new_common(fd, opts, (struct fuse_operations *) op, + sizeof(struct fuse_operations_compat2), 21); +} + +struct fuse *fuse_new_compat1(int fd, int flags, + const struct fuse_operations_compat1 *op) +{ + const char *opts = NULL; + if (flags & FUSE_DEBUG_COMPAT1) + opts = "debug"; + return fuse_new_common(fd, opts, (struct fuse_operations *) op, + sizeof(struct fuse_operations_compat1), 11); +} + __asm__(".symver fuse_exited,__fuse_exited@"); __asm__(".symver fuse_process_cmd,__fuse_process_cmd@"); __asm__(".symver fuse_read_cmd,__fuse_read_cmd@"); __asm__(".symver fuse_set_getcontext_func,__fuse_set_getcontext_func@"); __asm__(".symver fuse_new_compat2,fuse_new@"); __asm__(".symver fuse_new_compat22,fuse_new@FUSE_2.2"); + +#else /* __FreeBSD__ */ + +static int fuse_do_open(struct fuse *f, char *path, struct fuse_file_info *fi) +{ + return f->op.open(path, fi); +} + +static void fuse_do_release(struct fuse *f, char *path, + struct fuse_file_info *fi) +{ + f->op.release(path ? path : "-", fi); +} + +static int fuse_do_opendir(struct fuse *f, char *path, + struct fuse_file_info *fi) +{ + return f->op.opendir(path, fi); +} + +static int fuse_do_statfs(struct fuse *f, char *path, struct statvfs *buf) +{ + return f->op.statfs(path, buf); +} + +#endif /* __FreeBSD__ */ diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 31c2789..4e5c2ac 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -8,7 +8,6 @@ #include #include "fuse_lowlevel.h" -#include "fuse_lowlevel_compat.h" #include "fuse_kernel.h" #include @@ -17,7 +16,6 @@ #include #include #include -#include #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) @@ -198,18 +196,6 @@ static void convert_statfs(const struct statvfs *stbuf, kstatfs->namelen = stbuf->f_namemax; } -static void convert_statfs_compat(const struct statfs *stbuf, - struct fuse_kstatfs *kstatfs) -{ - kstatfs->bsize = stbuf->f_bsize; - kstatfs->blocks = stbuf->f_blocks; - kstatfs->bfree = stbuf->f_bfree; - kstatfs->bavail = stbuf->f_bavail; - kstatfs->files = stbuf->f_files; - kstatfs->ffree = stbuf->f_ffree; - kstatfs->namelen = stbuf->f_namelen; -} - static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) { return send_reply(req, 0, arg, argsize); @@ -268,16 +254,6 @@ static void fill_open(struct fuse_open_out *arg, arg->open_flags |= FOPEN_KEEP_CACHE; } -static void fill_open_compat(struct fuse_open_out *arg, - const struct fuse_file_info_compat *f) -{ - arg->fh = f->fh; - if (f->direct_io) - arg->open_flags |= FOPEN_DIRECT_IO; - if (f->keep_cache) - arg->open_flags |= FOPEN_KEEP_CACHE; -} - int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) { struct fuse_entry_out arg; @@ -328,16 +304,6 @@ int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) return send_reply_ok(req, &arg, sizeof(arg)); } -int fuse_reply_open_compat(fuse_req_t req, - const struct fuse_file_info_compat *f) -{ - struct fuse_open_out arg; - - memset(&arg, 0, sizeof(arg)); - fill_open_compat(&arg, f); - return send_reply_ok(req, &arg, sizeof(arg)); -} - int fuse_reply_write(fuse_req_t req, size_t count) { struct fuse_write_out arg; @@ -363,16 +329,6 @@ int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) return send_reply_ok(req, &arg, sizeof(arg)); } -int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf) -{ - struct fuse_statfs_out arg; - - memset(&arg, 0, sizeof(arg)); - convert_statfs_compat(stbuf, &arg.st); - - return send_reply_ok(req, &arg, sizeof(arg)); -} - int fuse_reply_xattr(fuse_req_t req, size_t count) { struct fuse_getxattr_out arg; @@ -1006,5 +962,54 @@ struct fuse_session *fuse_lowlevel_new(const char *opts, return NULL; } +#ifndef __FreeBSD__ + +#include "fuse_lowlevel_compat.h" + +static void fill_open_compat(struct fuse_open_out *arg, + const struct fuse_file_info_compat *f) +{ + arg->fh = f->fh; + if (f->direct_io) + arg->open_flags |= FOPEN_DIRECT_IO; + if (f->keep_cache) + arg->open_flags |= FOPEN_KEEP_CACHE; +} + +static void convert_statfs_compat(const struct statfs *stbuf, + struct fuse_kstatfs *kstatfs) +{ + kstatfs->bsize = stbuf->f_bsize; + kstatfs->blocks = stbuf->f_blocks; + kstatfs->bfree = stbuf->f_bfree; + kstatfs->bavail = stbuf->f_bavail; + kstatfs->files = stbuf->f_files; + kstatfs->ffree = stbuf->f_ffree; + kstatfs->namelen = stbuf->f_namelen; +} + +int fuse_reply_open_compat(fuse_req_t req, + const struct fuse_file_info_compat *f) +{ + struct fuse_open_out arg; + + memset(&arg, 0, sizeof(arg)); + fill_open_compat(&arg, f); + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf) +{ + struct fuse_statfs_out arg; + + memset(&arg, 0, sizeof(arg)); + convert_statfs_compat(stbuf, &arg.st); + + return send_reply_ok(req, &arg, sizeof(arg)); +} + + __asm__(".symver fuse_reply_statfs_compat,fuse_reply_statfs@FUSE_2.4"); __asm__(".symver fuse_reply_open_compat,fuse_reply_open@FUSE_2.4"); + +#endif __FreeBSD__ diff --git a/lib/helper.c b/lib/helper.c index 79616dc..bb8243c 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -7,7 +7,6 @@ */ #include "fuse_i.h" -#include "fuse_compat.h" #include #include @@ -353,25 +352,6 @@ struct fuse *fuse_setup(int argc, char *argv[], multithreaded, fd, 0); } -struct fuse *fuse_setup_compat22(int argc, char *argv[], - const struct fuse_operations_compat22 *op, - size_t op_size, char **mountpoint, - int *multithreaded, int *fd) -{ - return fuse_setup_common(argc, argv, (struct fuse_operations *) op, - op_size, mountpoint, multithreaded, fd, 22); -} - -struct fuse *fuse_setup_compat2(int argc, char *argv[], - const struct fuse_operations_compat2 *op, - char **mountpoint, int *multithreaded, - int *fd) -{ - return fuse_setup_common(argc, argv, (struct fuse_operations *) op, - sizeof(struct fuse_operations_compat2), - mountpoint, multithreaded, fd, 21); -} - void fuse_teardown(struct fuse *fuse, int fd, char *mountpoint) { (void) fd; @@ -419,6 +399,36 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, return fuse_main_common(argc, argv, op, op_size, 0); } +#undef fuse_main +int fuse_main(void) +{ + fprintf(stderr, "fuse_main(): This function does not exist\n"); + return -1; +} + +#ifndef __FreeBSD__ + +#include "fuse_compat.h" + +struct fuse *fuse_setup_compat22(int argc, char *argv[], + const struct fuse_operations_compat22 *op, + size_t op_size, char **mountpoint, + int *multithreaded, int *fd) +{ + return fuse_setup_common(argc, argv, (struct fuse_operations *) op, + op_size, mountpoint, multithreaded, fd, 22); +} + +struct fuse *fuse_setup_compat2(int argc, char *argv[], + const struct fuse_operations_compat2 *op, + char **mountpoint, int *multithreaded, + int *fd) +{ + return fuse_setup_common(argc, argv, (struct fuse_operations *) op, + sizeof(struct fuse_operations_compat2), + mountpoint, multithreaded, fd, 21); +} + int fuse_main_real_compat22(int argc, char *argv[], const struct fuse_operations_compat22 *op, size_t op_size) @@ -427,13 +437,6 @@ int fuse_main_real_compat22(int argc, char *argv[], op_size, 22); } -#undef fuse_main -int fuse_main(void) -{ - fprintf(stderr, "fuse_main(): This function does not exist\n"); - return -1; -} - void fuse_main_compat1(int argc, char *argv[], const struct fuse_operations_compat1 *op) { @@ -453,3 +456,5 @@ __asm__(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2"); __asm__(".symver fuse_teardown,__fuse_teardown@"); __asm__(".symver fuse_main_compat2,fuse_main@"); __asm__(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2"); + +#endif /* __FreeBSD__ */ diff --git a/lib/mount_bsd.c b/lib/mount_bsd.c index 0111b14..4b8ce95 100644 --- a/lib/mount_bsd.c +++ b/lib/mount_bsd.c @@ -24,11 +24,12 @@ void fuse_unmount(const char *mountpoint) FILE *sf; int rv; char *seekscript = - "/usr/bin/fstat /dev/fuse* |\n" - "/usr/bin/awk '{if ($3 == %d) print $10}' |\n" - "/usr/bin/sort |\n" - "/usr/bin/uniq |\n" - "/usr/bin/awk '{ i+=1; if(i > 1){ exit (1); }; printf; }; END{if (i==0) exit (1)}'"; + "/usr/bin/fstat /dev/fuse* | " + "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; " + " { if ($3 == %d) print $10; }' | " + "/usr/bin/sort | " + "/usr/bin/uniq | " + "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'"; asprintf(&ssc, seekscript, getpid()); @@ -61,7 +62,7 @@ int fuse_mount(const char *mountpoint, const char *opts) fd = strtol(fdnam, &ep, 10); if (*ep != '\0') { - fprintf(stderr, "invalid value given in FUSE_DEV_FD"); + fprintf(stderr, "invalid value given in FUSE_DEV_FD\n"); return -1; } @@ -118,6 +119,7 @@ mount: argv[a++] = mountpoint; argv[a++] = NULL; setenv("MOUNT_FUSEFS_SAFE", "1", 1); + setenv("MOUNT_FUSEFS_NOINTERACTIVE", "1", 1); execvp(mountprog, (char **) argv); perror("fuse: failed to exec mount program"); exit(1); -- 2.30.2