1 /* vi: set sw=4 ts=4: */
3 * nfsmount.c -- Linux NFS mount
4 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
6 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
9 * numbers to be specified on the command line.
11 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
12 * Omit the call to connect() for Linux version 1.3.11 or later.
14 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
15 * Implemented the "bg", "fg" and "retry" mount options for NFS.
17 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
18 * - added Native Language Support
20 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
27 #include <sys/utsname.h>
31 #include <rpc/pmap_prot.h>
32 #include <rpc/pmap_clnt.h>
34 /* This is just a warning of a common mistake. Possibly this should be a
35 * uclibc faq entry rather than in busybox... */
36 #if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
37 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
40 /* former nfsmount.h */
43 #define MNTPATHLEN 1024
48 typedef char fhandle[FHSIZE];
51 unsigned int fhandle3_len;
63 MNT3ERR_NAMETOOLONG = 63,
64 MNT3ERR_NOTSUPP = 10004,
65 MNT3ERR_SERVERFAULT = 10006,
67 typedef enum mountstat3 mountstat3;
70 unsigned int fhs_status;
75 typedef struct fhstatus fhstatus;
80 unsigned int auth_flavours_len;
81 char *auth_flavours_val;
84 typedef struct mountres3_ok mountres3_ok;
87 mountstat3 fhs_status;
89 mountres3_ok mountinfo;
92 typedef struct mountres3 mountres3;
94 typedef char *dirpath;
98 typedef struct mountbody *mountlist;
102 dirpath ml_directory;
105 typedef struct mountbody mountbody;
107 typedef struct groupnode *groups;
113 typedef struct groupnode groupnode;
115 typedef struct exportnode *exports;
122 typedef struct exportnode exportnode;
135 typedef struct ppathcnf ppathcnf;
137 #define MOUNTPROG 100005
140 #define MOUNTPROC_NULL 0
141 #define MOUNTPROC_MNT 1
142 #define MOUNTPROC_DUMP 2
143 #define MOUNTPROC_UMNT 3
144 #define MOUNTPROC_UMNTALL 4
145 #define MOUNTPROC_EXPORT 5
146 #define MOUNTPROC_EXPORTALL 6
148 #define MOUNTVERS_POSIX 2
150 #define MOUNTPROC_PATHCONF 7
154 #define MOUNTPROC3_NULL 0
155 #define MOUNTPROC3_MNT 1
156 #define MOUNTPROC3_DUMP 2
157 #define MOUNTPROC3_UMNT 3
158 #define MOUNTPROC3_UMNTALL 4
159 #define MOUNTPROC3_EXPORT 5
161 /* former nfsmount.h ends */
173 // S_QUOTA = 128, /* Quota initialized for file/directory/symlink */
178 * We want to be able to compile mount on old kernels in such a way
179 * that the binary will work well on more recent kernels.
180 * Thus, if necessary we teach nfsmount.c the structure of new fields
181 * that will come later.
183 * Moreover, the new kernel includes conflict with glibc includes
184 * so it is easiest to ignore the kernel altogether (at compile time).
192 unsigned char data[64];
195 struct nfs_mount_data {
198 struct nfs2_fh old_root; /* 1 */
204 int acregmin; /* 1 */
205 int acregmax; /* 1 */
206 int acdirmin; /* 1 */
207 int acdirmax; /* 1 */
208 struct sockaddr_in addr; /* 1 */
209 char hostname[256]; /* 1 */
211 unsigned int bsize; /* 3 */
212 struct nfs3_fh root; /* 4 */
215 /* bits in the flags field */
217 NFS_MOUNT_SOFT = 0x0001, /* 1 */
218 NFS_MOUNT_INTR = 0x0002, /* 1 */
219 NFS_MOUNT_SECURE = 0x0004, /* 1 */
220 NFS_MOUNT_POSIX = 0x0008, /* 1 */
221 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
222 NFS_MOUNT_NOAC = 0x0020, /* 1 */
223 NFS_MOUNT_TCP = 0x0040, /* 2 */
224 NFS_MOUNT_VER3 = 0x0080, /* 3 */
225 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
226 NFS_MOUNT_NONLM = 0x0200 /* 3 */
229 #define HAVE_inet_aton
232 * We need to translate between nfs status return values and
233 * the local errno values which may not be the same.
235 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
236 * "after #include <errno.h> the symbol errno is reserved for any use,
237 * it cannot even be used as a struct tag or field name".
241 #define EDQUOT ENOSPC
244 // Convert each NFSERR_BLAH into EBLAH
246 static const struct {
250 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
251 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
252 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
253 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
256 static char *nfs_strerror(int status)
259 static char buf[256];
261 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
262 if (nfs_errtbl[i].stat == status)
263 return strerror(nfs_errtbl[i].errnum);
265 sprintf(buf, "unknown nfs status return value: %d", status);
269 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
271 if (!xdr_opaque(xdrs, objp, FHSIZE))
276 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
278 if (!xdr_u_int(xdrs, &objp->fhs_status))
280 switch (objp->fhs_status) {
282 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
291 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
293 if (!xdr_string(xdrs, objp, MNTPATHLEN))
298 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
300 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
305 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
307 if (!xdr_fhandle3(xdrs, &objp->fhandle))
309 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
310 sizeof (int), (xdrproc_t) xdr_int))
315 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
317 if (!xdr_enum(xdrs, (enum_t *) objp))
322 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
324 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
326 switch (objp->fhs_status) {
328 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
337 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
340 * nfs_mount_version according to the sources seen at compile time.
342 static int nfs_mount_version;
343 static int kernel_version;
346 * Unfortunately, the kernel prints annoying console messages
347 * in case of an unexpected nfs mount version (instead of
348 * just returning some error). Therefore we'll have to try
349 * and figure out what version the kernel expects.
352 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
353 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
354 * nfs_mount_version: version this source and running kernel can handle
357 find_kernel_nfs_mount_version(void)
362 nfs_mount_version = 4; /* default */
364 kernel_version = get_linux_version_code();
365 if (kernel_version) {
366 if (kernel_version < KERNEL_VERSION(2,1,32))
367 nfs_mount_version = 1;
368 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
369 (kernel_version >= KERNEL_VERSION(2,3,0) &&
370 kernel_version < KERNEL_VERSION(2,3,99)))
371 nfs_mount_version = 3;
372 /* else v4 since 2.3.99pre4 */
377 get_mountport(struct sockaddr_in *server_addr,
379 long unsigned version,
383 struct pmaplist *pmap;
384 static struct pmap p = {0, 0, 0, 0};
386 server_addr->sin_port = PMAPPORT;
387 pmap = pmap_getmaps(server_addr);
389 if (version > MAX_NFSPROT)
390 version = MAX_NFSPROT;
399 if (pmap->pml_map.pm_prog != prog)
401 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
403 if (version > 2 && pmap->pml_map.pm_vers != version)
405 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
407 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
408 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
409 (port && pmap->pml_map.pm_port != port))
411 memcpy(&p, &pmap->pml_map, sizeof(p));
413 pmap = pmap->pml_next;
416 p.pm_vers = MOUNTVERS;
418 p.pm_port = MOUNTPORT;
420 p.pm_prot = IPPROTO_TCP;
424 static int daemonize(void)
428 if (pid < 0) /* error */
430 if (pid > 0) /* parent */
433 fd = xopen(bb_dev_null, O_RDWR);
437 if (fd > 2) close(fd);
439 openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
440 logmode = LOGMODE_SYSLOG;
445 static inline int we_saw_this_host_before(const char *hostname)
450 /* RPC strerror analogs are terminally idiotic:
451 * *mandatory* prefix and \n at end.
452 * This hopefully helps. Usage:
453 * error_msg_rpc(clnt_*error*(" ")) */
454 static void error_msg_rpc(const char *msg)
457 while (msg[0] == ' ' || msg[0] == ':') msg++;
459 while (len && msg[len-1] == '\n') len--;
460 bb_error_msg("%.*s", len, msg);
463 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
469 struct nfs_mount_data data;
472 struct sockaddr_in server_addr;
473 struct sockaddr_in mount_server_addr;
476 struct fhstatus nfsv2;
477 struct mountres3 nfsv3;
499 find_kernel_nfs_mount_version();
507 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
509 filteropts = xstrdup(filteropts); /* going to trash it later... */
511 hostname = xstrdup(mp->mnt_fsname);
512 /* mount_main() guarantees that ':' is there */
513 s = strchr(hostname, ':');
516 /* Ignore all but first hostname in replicated mounts
517 until they can be fully supported. (mack@sgi.com) */
518 s = strchr(hostname, ',');
521 bb_error_msg("warning: multiple hostnames not supported");
524 server_addr.sin_family = AF_INET;
525 #ifdef HAVE_inet_aton
526 if (!inet_aton(hostname, &server_addr.sin_addr))
529 hp = gethostbyname(hostname);
531 bb_herror_msg("%s", hostname);
534 if (hp->h_length > sizeof(struct in_addr)) {
535 bb_error_msg("got bad hp->h_length");
536 hp->h_length = sizeof(struct in_addr);
538 memcpy(&server_addr.sin_addr,
539 hp->h_addr, hp->h_length);
542 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
544 /* add IP address to mtab options for use when unmounting */
546 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
547 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
549 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
550 mp->mnt_opts[0] ? "," : "",
551 inet_ntoa(server_addr.sin_addr));
556 /* Set default options.
557 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
558 * let the kernel decide.
559 * timeo is filled in after we know whether it'll be TCP or UDP. */
560 memset(&data, 0, sizeof(data));
566 data.namlen = NAME_MAX;
575 retry = 10000; /* 10000 minutes ~ 1 week */
578 mountprog = MOUNTPROG;
587 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
588 char *opteq = strchr(opt, '=');
590 int val = atoi(opteq + 1);
592 if (!strcmp(opt, "rsize"))
594 else if (!strcmp(opt, "wsize"))
596 else if (!strcmp(opt, "timeo"))
598 else if (!strcmp(opt, "retrans"))
600 else if (!strcmp(opt, "acregmin"))
602 else if (!strcmp(opt, "acregmax"))
604 else if (!strcmp(opt, "acdirmin"))
606 else if (!strcmp(opt, "acdirmax"))
608 else if (!strcmp(opt, "actimeo")) {
614 else if (!strcmp(opt, "retry"))
616 else if (!strcmp(opt, "port"))
618 else if (!strcmp(opt, "mountport"))
620 else if (!strcmp(opt, "mounthost"))
621 mounthost = xstrndup(opteq+1,
622 strcspn(opteq+1," \t\n\r,"));
623 else if (!strcmp(opt, "mountprog"))
625 else if (!strcmp(opt, "mountvers"))
627 else if (!strcmp(opt, "nfsprog"))
629 else if (!strcmp(opt, "nfsvers") ||
630 !strcmp(opt, "vers"))
632 else if (!strcmp(opt, "proto")) {
633 if (!strncmp(opteq+1, "tcp", 3))
635 else if (!strncmp(opteq+1, "udp", 3))
638 bb_error_msg("warning: unrecognized proto= option");
639 } else if (!strcmp(opt, "namlen")) {
640 if (nfs_mount_version >= 2)
643 bb_error_msg("warning: option namlen is not supported\n");
644 } else if (!strcmp(opt, "addr"))
647 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
653 if (!strncmp(opt, "no", 2)) {
657 if (!strcmp(opt, "bg"))
659 else if (!strcmp(opt, "fg"))
661 else if (!strcmp(opt, "soft"))
663 else if (!strcmp(opt, "hard"))
665 else if (!strcmp(opt, "intr"))
667 else if (!strcmp(opt, "posix"))
669 else if (!strcmp(opt, "cto"))
671 else if (!strcmp(opt, "ac"))
673 else if (!strcmp(opt, "tcp"))
675 else if (!strcmp(opt, "udp"))
677 else if (!strcmp(opt, "lock")) {
678 if (nfs_mount_version >= 3)
681 bb_error_msg("warning: option nolock is not supported");
683 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
688 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
690 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
691 | (intr ? NFS_MOUNT_INTR : 0)
692 | (posix ? NFS_MOUNT_POSIX : 0)
693 | (nocto ? NFS_MOUNT_NOCTO : 0)
694 | (noac ? NFS_MOUNT_NOAC : 0);
695 if (nfs_mount_version >= 2)
696 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
697 if (nfs_mount_version >= 3)
698 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
699 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
700 bb_error_msg("NFSv%d not supported", nfsvers);
703 if (nfsvers && !mountvers)
704 mountvers = (nfsvers < 3) ? 1 : nfsvers;
705 if (nfsvers && nfsvers < mountvers) {
709 /* Adjust options if none specified */
711 data.timeo = tcp ? 70 : 7;
713 #ifdef NFS_MOUNT_DEBUG
714 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
715 data.rsize, data.wsize, data.timeo, data.retrans);
716 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
717 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
718 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
719 port, bg, retry, data.flags);
720 printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
721 mountprog, mountvers, nfsprog, nfsvers);
722 printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
723 (data.flags & NFS_MOUNT_SOFT) != 0,
724 (data.flags & NFS_MOUNT_INTR) != 0,
725 (data.flags & NFS_MOUNT_POSIX) != 0,
726 (data.flags & NFS_MOUNT_NOCTO) != 0,
727 (data.flags & NFS_MOUNT_NOAC) != 0);
729 (data.flags & NFS_MOUNT_TCP) != 0);
732 data.version = nfs_mount_version;
734 if (vfsflags & MS_REMOUNT)
738 * If the previous mount operation on the same host was
739 * backgrounded, and the "bg" for this mount is also set,
740 * give up immediately, to avoid the initial timeout.
742 if (bg && we_saw_this_host_before(hostname)) {
743 daemonized = daemonize(); /* parent or error */
744 if (daemonized <= 0) { /* parent or error */
745 retval = -daemonized;
750 /* create mount daemon client */
751 /* See if the nfs host = mount host. */
753 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
754 mount_server_addr.sin_family = AF_INET;
755 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
757 hp = gethostbyname(mounthost);
759 bb_herror_msg("%s", mounthost);
762 if (hp->h_length > sizeof(struct in_addr)) {
763 bb_error_msg("got bad hp->h_length?");
764 hp->h_length = sizeof(struct in_addr);
766 mount_server_addr.sin_family = AF_INET;
767 memcpy(&mount_server_addr.sin_addr,
768 hp->h_addr, hp->h_length);
774 * The following loop implements the mount retries. When the mount
775 * times out, and the "bg" option is set, we background ourself
776 * and continue trying.
778 * The case where the mount point is not present and the "bg"
779 * option is set, is treated as a timeout. This is done to
780 * support nested mounts.
782 * The "retry" count specified by the user is the number of
783 * minutes to retry before giving up.
786 struct timeval total_timeout;
787 struct timeval retry_timeout;
793 retry_timeout.tv_sec = 3;
794 retry_timeout.tv_usec = 0;
795 total_timeout.tv_sec = 20;
796 total_timeout.tv_usec = 0;
797 timeout = time(NULL) + 60 * retry;
801 /* be careful not to use too many CPU cycles */
805 pm_mnt = get_mountport(&mount_server_addr,
810 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
812 /* contact the mount daemon via TCP */
813 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
816 switch (pm_mnt->pm_prot) {
818 mclient = clntudp_create(&mount_server_addr,
825 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
828 mclient = clnttcp_create(&mount_server_addr,
837 if (!daemonized && prevt == 0)
838 error_msg_rpc(clnt_spcreateerror(" "));
840 enum clnt_stat clnt_stat;
841 /* try to mount hostname:pathname */
842 mclient->cl_auth = authunix_create_default();
844 /* make pointers in xdr_mountres3 NULL so
845 * that xdr_array allocates memory for us
847 memset(&status, 0, sizeof(status));
849 if (pm_mnt->pm_vers == 3)
850 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
851 (xdrproc_t) xdr_dirpath,
853 (xdrproc_t) xdr_mountres3,
857 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
858 (xdrproc_t) xdr_dirpath,
860 (xdrproc_t) xdr_fhstatus,
864 if (clnt_stat == RPC_SUCCESS)
865 goto prepare_kernel_data; /* we're done */
866 if (errno != ECONNREFUSED) {
867 error_msg_rpc(clnt_sperror(mclient, " "));
868 goto fail; /* don't retry */
870 /* Connection refused */
871 if (!daemonized && prevt == 0) /* print just once */
872 error_msg_rpc(clnt_sperror(mclient, " "));
873 auth_destroy(mclient->cl_auth);
874 clnt_destroy(mclient);
879 /* Timeout. We are going to retry... maybe */
884 daemonized = daemonize();
885 if (daemonized <= 0) { /* parent or error */
886 retval = -daemonized;
893 /* TODO error message */
902 if (status.nfsv2.fhs_status != 0) {
903 bb_error_msg("%s:%s failed, reason given by server: %s",
905 nfs_strerror(status.nfsv2.fhs_status));
908 memcpy(data.root.data,
909 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
911 data.root.size = NFS_FHSIZE;
912 memcpy(data.old_root.data,
913 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
916 fhandle3 *my_fhandle;
917 if (status.nfsv3.fhs_status != 0) {
918 bb_error_msg("%s:%s failed, reason given by server: %s",
920 nfs_strerror(status.nfsv3.fhs_status));
923 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
924 memset(data.old_root.data, 0, NFS_FHSIZE);
925 memset(&data.root, 0, sizeof(data.root));
926 data.root.size = my_fhandle->fhandle3_len;
927 memcpy(data.root.data,
928 (char *) my_fhandle->fhandle3_val,
929 my_fhandle->fhandle3_len);
931 data.flags |= NFS_MOUNT_VER3;
934 /* create nfs socket for kernel */
937 if (nfs_mount_version < 3) {
938 bb_error_msg("NFS over TCP is not supported");
941 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
943 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
945 bb_perror_msg("nfs socket");
948 if (bindresvport(fsock, 0) < 0) {
949 bb_perror_msg("nfs bindresvport");
953 server_addr.sin_port = PMAPPORT;
954 port = pmap_getport(&server_addr, nfsprog, nfsvers,
955 tcp ? IPPROTO_TCP : IPPROTO_UDP);
958 #ifdef NFS_MOUNT_DEBUG
960 printf("used portmapper to find NFS port\n");
963 #ifdef NFS_MOUNT_DEBUG
964 printf("using port %d for nfs daemon\n", port);
966 server_addr.sin_port = htons(port);
968 /* prepare data structure for kernel */
971 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
972 strncpy(data.hostname, hostname, sizeof(data.hostname));
976 auth_destroy(mclient->cl_auth);
977 clnt_destroy(mclient);
981 /* We must wait until mount directory is available */
984 while (stat(mp->mnt_dir, &statbuf) == -1) {
986 daemonized = daemonize();
987 if (daemonized <= 0) { /* parent or error */
988 retval = -daemonized;
992 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
999 do_mount: /* perform actual mount */
1001 mp->mnt_type = "nfs";
1002 retval = mount_it_now(mp, vfsflags, (char*)&data);
1009 auth_destroy(mclient->cl_auth);
1010 clnt_destroy(mclient);