Esempio n. 1
0
static void
check_version (void)
{
    int version;

    rpc_get (msock, RPC_INT, &version, RPC_END);
    if (version >= 1 && version <= RPC_PROGVER)
	rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
    else
	rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);

    clnt_version = version;
}
Esempio n. 2
0
/* FIXME: Implement the anonymous login */
static void
do_login (void)
{
    char *username;
    char *password;
    int result;

    rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
	     &username, RPC_END);
    if (verbose)
	printf ("username: %s\n", username);

    if (r_auth) {
	logged_in = do_rauth (msock);
	if (logged_in) {
	    login_reply (logged_in);
	    return;
	}
    }
    rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
    rpc_get (msock, RPC_INT, &result, RPC_END);
    if (result == MC_QUIT)
	DO_QUIT_VOID ();
    if (result != MC_PASS) {
	if (verbose)
	    printf ("do_login: Unknown response: %d\n", result);
	DO_QUIT_VOID ();
    }
    rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
    logged_in = do_auth (username, password);
    endpwent ();
    login_reply (logged_in);
}
Esempio n. 3
0
/* send a request for a directory item */
int
nfs_readdir(struct cookie *pfh, int cookie, int size,
	void (*func) (uintptr_t , int, int, struct nfs_filename *, int),
	uintptr_t token)
{
    readdirargs_t args;

    struct pbuf *pbuf;
    
    /* now the user data struct is setup, do some call stuff! */
    pbuf = initbuf(NFS_NUMBER, NFS_VERSION, NFSPROC_READDIR);

    /* copy the buffer */
    memcpy(&args.dir, pfh, sizeof(struct cookie));
    
    /* set the cookie */
    args.cookie = cookie;
    args.count = size;

    /* copy the arguments into the packet */
    addtobuf(pbuf, (char*) &args, sizeof(args));

    /* make the call! */
    return rpc_send(pbuf, nfs_port, nfs_readdir_cb, func, token);
}
Esempio n. 4
0
int rpc_call(struct rpc *r, int func_id,
            void *in_arg, size_t in_len,
            void *out_arg, size_t out_len)
{
    rpc_packet_format(r, func_id);
    return rpc_send(r, in_arg, in_len);
}
Esempio n. 5
0
int rpc_call(struct rpc *r, uint32_t msg_id,
                const void *in_arg, size_t in_len,
                void *out_arg, size_t out_len)
{
    if (!r) {
        loge("invalid parament!\n");
        return -1;
    }
    size_t pkt_len = pack_msg(&r->send_pkt, msg_id, in_arg, in_len);
    if (pkt_len == 0) {
        loge("pack_msg failed!\n");
        return -1;
    }
    if (0 > rpc_send(r, in_arg, in_len)) {
        loge("skt_send failed, fd = %d!\n", r->fd);
        return -1;
    }
    if (IS_RPC_MSG_NEED_RETURN(msg_id)) {
        if (thread_sem_wait(r->dispatch_thread, 2000) == -1) {
            loge("wait response failed %d:%s\n", errno, strerror(errno));
            return -1;
        }
        logi("recv_pkt.len = %d\n", r->recv_pkt.header.payload_len);
        memcpy(out_arg, r->recv_pkt.payload, out_len);
    } else {

    }
    return 0;
}
Esempio n. 6
0
int
nfs_write(struct cookie *fh, int offset, int count, void *data,
     void (*func) (uintptr_t, int, fattr_t *),
     uintptr_t token)
{
    struct pbuf *pbuf;
    writeargs_t args;
    
    /* now the user data struct is setup, do some call stuff! */
    pbuf = initbuf(NFS_NUMBER, NFS_VERSION, NFSPROC_WRITE);

    /* copy in the fhandle */
    memcpy(&args.file, (char*) fh, sizeof(struct cookie));
    
    args.offset = offset;
    args.beginoffset = 0; /* unused as per RFC */
    args.totalcount = 0;  /* unused as per RFC */
    
    /* add them to the buffer */
    addtobuf(pbuf, (char*) &args, sizeof(args));
    
    /* put the data in */
    adddata(pbuf, data, count);

    return rpc_send(pbuf, nfs_port, nfs_write_cb, func, token);
}
Esempio n. 7
0
static void
send_stat_info (struct stat *st)
{
    long mylong;
    int blocks =
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
	st->st_blocks;
#else
	st->st_size / 1024;
#endif

#ifdef HAVE_STRUCT_STAT_ST_RDEV
    mylong = st->st_rdev;
#else
    mylong = 0;
#endif
    rpc_send (msock, RPC_INT, (long) mylong, RPC_INT, (long) st->st_ino,
	      RPC_INT, (long) st->st_mode, RPC_INT, (long) st->st_nlink,
	      RPC_INT, (long) st->st_uid, RPC_INT, (long) st->st_gid,
	      RPC_INT, (long) st->st_size, RPC_INT, (long) blocks,
	      RPC_END);
    send_time (msock, st->st_atime);
    send_time (msock, st->st_mtime);
    send_time (msock, st->st_ctime);
}
Esempio n. 8
0
static void
do_read (void)
{
    int handle, count, n;
    void *data;

    rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
    data = malloc (count);
    if (!data) {
	send_status (-1, ENOMEM);
	return;
    }
    if (verbose)
	printf ("count=%d\n", count);
    n = read (handle, data, count);
    if (verbose)
	printf ("result=%d\n", n);
    if (n < 0) {
	send_status (-1, errno);
	return;
    }
    send_status (n, 0);
    rpc_send (msock, RPC_BLOCK, n, data, RPC_END);

    g_free (data);
}
Esempio n. 9
0
File: rpcd.c Progetto: gozfree/src
struct rpc *rpc_connect_create(struct rpcd *rpcd, int fd, uint32_t ip, uint16_t port)
{
    char str_ip[INET_ADDRSTRLEN];
    char uuid[MAX_UUID_LEN];
    int ret;

    struct rpc *r = (struct rpc *)calloc(1, sizeof(struct rpc));
    if (!r) {
        loge("malloc failed!\n");
        return NULL;
    }
    r->fd = fd;
    create_uuid(uuid, MAX_UUID_LEN, fd, ip, port);
    struct gevent *e = gevent_create(fd, on_recv, NULL, on_error, (void *)r);
    if (-1 == gevent_add(rpcd->evbase, e)) {
        loge("event_add failed!\n");
    }
    r->ev = e;
    rpc_header_format(r, uuid, uuid, 0, 0);
    ret = rpc_send(r, uuid, MAX_UUID_LEN);
    if (ret != MAX_UUID_LEN) {
        loge("rpc_send failed!\n");
    }
    rpcd_connect_add(rpcd, r, fd, uuid);
    skt_addr_ntop(str_ip, ip);
    logi("on_connect fd = %d, remote_addr = %s:%d, uuid=%s\n", fd, str_ip, port, uuid);

    return r;
}
Esempio n. 10
0
static void
send_time (int sock, time_t time)
{
    if (clnt_version == 1) {
	char *ct;
	int month;

	ct = ctime (&time);
	ct[3] = ct[10] = ct[13] = ct[16] = ct[19] = 0;

	/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
	if (ct[4] == 'J') {
	    if (ct[5] == 'a') {
		month = 0;
	    } else
		month = (ct[6] == 'n') ? 5 : 6;
	} else if (ct[4] == 'F') {
	    month = 1;
	} else if (ct[4] == 'M') {
	    month = (ct[6] == 'r') ? 2 : 5;
	} else if (ct[4] == 'A') {
	    month = (ct[5] == 'p') ? 3 : 7;
	} else if (ct[4] == 'S') {
	    month = 8;
	} else if (ct[4] == 'O') {
	    month = 9;
	} else if (ct[4] == 'N') {
	    month = 10;
	} else
	    month = 11;
	rpc_send (msock, RPC_INT, atoi (&ct[17]),	/* sec */
		  RPC_INT, atoi (&ct[14]),	/* min */
		  RPC_INT, atoi (&ct[11]),	/* hour */
		  RPC_INT, atoi (&ct[8]),	/* mday */
		  RPC_INT, atoi (&ct[20]),	/* year */
		  RPC_INT, month,	/* month */
		  RPC_END);
    } else {
	long ltime = (long) time;
	char buf[BUF_SMALL];

	snprintf (buf, sizeof (buf), "%lx", ltime);
	rpc_send (msock, RPC_STRING, buf, RPC_END);
    }
}
Esempio n. 11
0
File: rpc.c Progetto: gapry/AOS
enum rpc_stat
rpc_call(struct pbuf *pbuf, int len, struct udp_pcb *pcb, 
     void (*func)(void *, uintptr_t, struct pbuf *), 
     void *callback, uintptr_t token)
{
    struct rpc_call_arg call_arg;
    struct rpc_queue *q_item;
    int time_out;
    enum rpc_stat stat;
    xid_t xid;

    /* If we give up early, we must ensure that the argument remains in memory
     * just in case the packet comes in later */
    assert(pcb);
    assert(pbuf);

    /* GeneratrSend the thing with the unlock frunction as a callback */
    call_arg.func = func;
    call_arg.callback = callback;
    call_arg.token = token;
    call_arg.complete = 0;

    /* Make the call */
    xid = extract_xid(pbuf);
    stat = rpc_send(pbuf, pbuf->tot_len, pcb, &rpc_call_cb, NULL, 
                   (uintptr_t)&call_arg);
    if(stat){
        return stat;
    }

    /* Wait for the response */
    time_out = RETRANSMIT_DELAY_MS * (CALL_RETRIES + 1);
    while(time_out >= 0){
        _usleep(CALL_TIMEOUT_MS * 1000);
        if(call_arg.complete) {
            /* Success */
            return 0;
        }
        rpc_timeout(CALL_TIMEOUT_MS);
        time_out -= CALL_TIMEOUT_MS;
    }

    /* If we get here, we have failed. Data is on the stack so make sure 
     * we remove references from the queue */
    q_item = get_from_queue(xid);
    assert(q_item);
    pbuf_free(q_item->pbuf);
    free(q_item);
    return RPCERR_COMM;
}
Esempio n. 12
0
/* Sends the complete directory listing, as well as the stat information */
static void
do_readdir (void)
{
    struct dirent *dirent;
    struct stat st;
    int handle, n;

    rpc_get (msock, RPC_INT, &handle, RPC_END);

    if (!handle) {
	rpc_send (msock, RPC_INT, 0, RPC_END);
	return;
    }

    /* We incremented it in opendir */
    handle--;

    while ((dirent = readdir (mcfs_DIR.dirs[handle]))) {
	int fname_len;
	char *fname;
	int length = NLENGTH (dirent);

	rpc_send (msock, RPC_INT, length, RPC_END);
	rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
	fname_len =
	    strlen (mcfs_DIR.names[handle]) + strlen (dirent->d_name) + 2;
	fname = malloc (fname_len);
	snprintf (fname, fname_len, "%s/%s", mcfs_DIR.names[handle],
		  dirent->d_name);
	n = lstat (fname, &st);
	g_free (fname);
	send_status (n, errno);
	if (n >= 0)
	    send_stat_info (&st);
    }
    rpc_send (msock, RPC_INT, 0, RPC_END);
}
Esempio n. 13
0
File: transport.c Progetto: gz/aos10
struct pbuf *
rpc_call(struct pbuf *pbuf, int port)
{
    L4_ThreadId_t from;

    opaque_auth_t auth;
    reply_stat r;

    /* Send the thing */
    rpc_send(pbuf, port, signal, NULL, L4_Myself().raw);

    /* We wait for a reply */
    L4_Wait(&from);
    pbuf_adv_arg(call_pbuf, 0, 8);

    /* check if it was an accepted reply */
    getfrombuf(call_pbuf, (char*) &r, sizeof(r));

    if(r != MSG_ACCEPTED) {
	debug( "Message NOT accepted (%d)\n", r );

	/* extract error code */
	getfrombuf(call_pbuf, (char*) &r, sizeof(r));
	debug( "Error code %d\n", r );
	
	if(r == 1) {
	    /* get the auth problem */
	    getfrombuf(call_pbuf, (char*) &r, sizeof(r));
	    debug( "auth_stat %d\n", r );
	}
	
	return 0;
    }
    
    /* and the auth data!*/
    getfrombuf(call_pbuf, (char*) &auth, sizeof(auth));

    debug("Got auth data. size is %d\n", auth.size);

    /* check its accept stat */
    getfrombuf(call_pbuf, (char*) &r, sizeof(r));
    
    if( r == SUCCESS )
	return call_pbuf;
    else {
	debug( "reply stat was %d\n", r );
	return NULL;
    }
}
Esempio n. 14
0
static void* do_rpc_thread(void *data) {
	LOG_ENTRY;

	struct rpc *rpc = data;
	if (!rpc || !rpc->handler) {
		RPC_PUTS("bad rpc");
		LOG_EXIT;
		return NULL;
	}
	
	rpc->active = 1;
	rpc_cond_signal(rpc);

	fd_set fds;

	while (rpc->active) {
		RPC_DEBUG("+loop");
		
		FD_ZERO(&fds);
		FD_SET(rpc->fd, &fds);
		FD_SET(rpc->pipefd[READ_END], &fds);

		select(max(rpc->fd, rpc->pipefd[READ_END]) + 1, &fds, NULL, NULL, NULL);
		pthread_mutex_lock(&rpc->fd_mutex);
		if (FD_ISSET(rpc->fd, &fds)) {
			RPC_DEBUG("receiving RPC message");
			if (rpc_recv(rpc) < 0) {
				RPC_ERROR("receive error");
				rpc->active = 0;
				goto handled;
			}
		}
		if (FD_ISSET(rpc->pipefd[READ_END], &fds)) {
			RPC_DEBUG("calling remote party");
			if (rpc_send(rpc) < 0) {
				RPC_ERROR("call error");
				rpc->active = 0;
				goto handled;
			}
		}
handled:
		pthread_mutex_unlock(&rpc->fd_mutex);
		RPC_DEBUG("-loop");
	}
	rpc_cond_signal(rpc);

	LOG_EXIT;
	return NULL;
}
Esempio n. 15
0
static void
do_opendir (void)
{
    int handle, i;
    char *arg;
    DIR *p;

    rpc_get (msock, RPC_STRING, &arg, RPC_END);

    if (mcfs_DIR.used == OPENDIR_HANDLES) {
	send_status (-1, ENFILE);	/* Error */
	g_free (arg);
	return;
    }

    handle = -1;
    for (i = 0; i < OPENDIR_HANDLES; i++) {
	if (mcfs_DIR.dirs[i] == 0) {
	    handle = i;
	    break;
	}
    }

    if (handle == -1) {
	send_status (-1, EMFILE);
	g_free (arg);
	if (!inetd_started)
	    fprintf (stderr,
		     "OOPS! you have found a bug in mc - do_opendir()!\n");
	return;
    }

    if (verbose)
	printf ("handle=%d\n", handle);
    p = opendir (arg);
    if (p) {
	mcfs_DIR.dirs[handle] = p;
	mcfs_DIR.names[handle] = arg;
	mcfs_DIR.used++;

	/* Because 0 is an error value */
	rpc_send (msock, RPC_INT, handle + 1, RPC_INT, 0, RPC_END);

    } else {
	send_status (-1, errno);
	g_free (arg);
    }
}
Esempio n. 16
0
int
nfs_getattr(struct cookie *fh,
       void (*func) (uintptr_t, int, fattr_t *), 
       uintptr_t token)
{
    struct pbuf *pbuf;

    /* now the user data struct is setup, do some call stuff! */
    pbuf = initbuf(NFS_NUMBER, NFS_VERSION, NFSPROC_GETATTR);
    
    /* put in the fhandle */
    addtobuf(pbuf, (char*) fh, sizeof(struct cookie));
    
    /* send it! */
    return rpc_send(pbuf, nfs_port, nfs_getattr_cb, func, token);
}
Esempio n. 17
0
static void
do_readlink (void)
{
    char buffer[2048];
    char *file;
    int n;

    rpc_get (msock, RPC_STRING, &file, RPC_END);
    n = readlink (file, buffer, 2048 - 1);
    send_status (n, errno);
    if (n >= 0) {
	buffer[n] = 0;
	rpc_send (msock, RPC_STRING, buffer, RPC_END);
    }
    g_free (file);
}
Esempio n. 18
0
/* remove a file named 'name' in directory 'cwd' */
int
nfs_remove(struct cookie *cwd, char *name, 
       void (*func) (uintptr_t, int), uintptr_t token)
{
    struct pbuf *pbuf;

    /* now the user data struct is setup, do some call stuff! */
    pbuf = initbuf(NFS_NUMBER, NFS_VERSION, NFSPROC_REMOVE);
    
    /* put in the fhandle */
    addtobuf(pbuf, (char*) cwd, sizeof(struct cookie));
    
    /* put in the name */
    addstring(pbuf, name);
    
    /* send it! */
    return rpc_send(pbuf, nfs_port, nfs_remove_cb, func, token);
}
Esempio n. 19
0
int
nfs_create(struct cookie *fh, char *name, sattr_t *sat,
     void (*func) (uintptr_t, int, struct cookie *, fattr_t *),
     uintptr_t token)
{
    struct pbuf *pbuf;
    
    /* now the user data struct is setup, do some call stuff! */
    pbuf = initbuf(NFS_NUMBER, NFS_VERSION, NFSPROC_CREATE);

    /* put in the fhandle */
    addtobuf(pbuf, (char*) fh, sizeof(struct cookie));
    
    /* put in the name */
    addstring(pbuf, name);
    
    addtobuf(pbuf, (char*) sat, sizeof(sattr_t));

    return rpc_send(pbuf, nfs_port, nfs_create_cb, func, token);
}
Esempio n. 20
0
File: rpcd.c Progetto: gozfree/src
int process_msg(struct rpc *r, struct iobuf *buf)
{
    int ret;
    msg_handler_t msg_handler;
    struct rpc_header *h = &r->packet.header;
    int msg_id = rpc_packet_parse(r);

    if (find_msg_handler(msg_id, &msg_handler) == 0) {
        msg_handler.cb(r, buf->addr, buf->len);
    } else {
        loge("no callback for this MSG ID in process_msg\n");
        char *valfd = (char *)dict_get(_rpcd->dict_uuid2fd, h->uuid_dst, NULL);
        if (!valfd) {
            loge("dict_get failed: key=%s\n", h->uuid_dst);
            return -1;
        }
        int dst_fd = strtol(valfd, NULL, 16);
        r->fd = dst_fd;
        ret = rpc_send(r, buf->addr, buf->len);
    }
    return ret;
}
Esempio n. 21
0
int
nfs_read(struct cookie *fh, int pos, int count,
     void (*func) (uintptr_t, int, fattr_t *, int, char *),
     uintptr_t token)
{
    struct pbuf *pbuf;
    readargs_t args;
    
    /* now the user data struct is setup, do some call stuff! */
    pbuf = initbuf(NFS_NUMBER, NFS_VERSION, NFSPROC_READ);

    /* copy in the fhandle */
    memcpy(&args.file, (char*) fh, sizeof(struct cookie));
    
    args.offset = pos;
    args.count = count;
    args.totalcount = 0;  /* unused as per RFC */
    
    /* add them to the buffer */
    addtobuf(pbuf, (char*) &args, sizeof(args));
    
    return rpc_send(pbuf, nfs_port, nfs_read_cb, func, token);
}
Esempio n. 22
0
/*
 * Man FIFO routine running in the FIFO
 * processes requests received
 * through the FIFO file repeatedly
 */
int fifo_process(char* msg_buf, int size, int* bytes_needed, void *sh,
					void** saved_state)
{
	rpc_export_t* exp;
	char* buf;
	int line_len;
	char *file_sep;
	struct text_chunk* p;
	struct rpc_struct* s;
	int r;
	int req_size;
	static rpc_ctx_t context; 

	DBG("process_fifo: called with %d bytes, offset %d: %.*s\n",
			size, (int)(long)*saved_state, size, msg_buf);
	/* search for the end of the request (\n\r) */
	if (size < 6){ /* min fifo request */
		*bytes_needed=6-size;
		return 0; /* we want more bytes, nothing processed */
	}
	for (r=1+(int)(long)*saved_state;r<size;r++){
		if ((msg_buf[r]=='\n' || msg_buf[r]=='\r') &&
			(msg_buf[r-1]=='\n'|| msg_buf[r-1]=='\r')){
			/* found double cr, or double lf => end of request */
			req_size=r;
			goto process;
		}
	}
	/* no end of request found => ask for more bytes */
	*bytes_needed=1;
	/* save current offset, to optimize search */
	*saved_state=(void*)(long)(r-1);
	return 0; /* we want again the whole buffer */
process:
	
	DBG("process_fifo  %d bytes request: %.*s\n", 
			req_size, req_size, msg_buf);
	file_sep = 0;
	context.method = 0;
	context.reply_file = 0;
	context.body = 0;
	context.code = 200;
	context.reason = "OK";
	context.reply_sent = 0;
	context.last = 0;
	context.line_no = 0;
	context.read_h.s=msg_buf;
	context.read_h.end=msg_buf+size;
	context.read_h.crt=msg_buf;
	context.send_h=(struct send_handle*)sh;
		     /* commands must look this way ':<command>:[filename]' */
		if (read_line(&buf, &line_len, &context.read_h) < 0) {
			     /* line breaking must have failed -- consume the rest
			      * and proceed to a new request
			      */
			ERR("Command expected\n");
			goto consume;
		}
		context.line_no++;
		if (line_len == 0) {
			DBG("Empty command received\n");
			goto consume;
		}
		if (line_len < 3) {
			ERR("Command must have at least 3 chars\n");
			goto consume;
		}
		if (*buf != CMD_SEPARATOR) {
			ERR("Command must begin with %c: %.*s\n", 
			    CMD_SEPARATOR, line_len, buf);
			goto consume;
		}

		context.method = buf + 1;
		file_sep = strchr(context.method, CMD_SEPARATOR);
		if (file_sep == NULL) {
			ERR("File separator missing\n");
			goto consume;
		}
		if (file_sep == context.method) {
			ERR("Empty command\n");
			goto consume;
		}
		if (*(file_sep + 1) == 0) context.reply_file = NULL; 
		else {
			context.reply_file = file_sep + 1;
			context.reply_file = trim_filename(context.reply_file);
			if (context.reply_file == 0) {
				ERR("Trimming filename\n");
				goto consume;
			}
		}
		     /* make command zero-terminated */
		*file_sep = 0;
		
		exp = find_rpc_export(context.method, 0);
		if (!exp || !exp->function) {
			DBG("Command %s not found\n", context.method);
			rpc_fault(&context, 500, "Command '%s' not found", context.method);
			goto consume;
		}

		exp->function(&func_param, &context);

	consume:
		if (!context.reply_sent) {
			rpc_send(&context);
		}

		if (context.reply_file) { 
			ctl_free(context.reply_file); 
			context.reply_file = 0; 
		}
		
		     /* Collect garbage (unescaped strings and structures) */
		while(context.strs) {
			p = context.strs;
			context.strs = context.strs->next;
			free_chunk(p);
		}

		while(context.structs) {
			s = context.structs;
			context.structs = context.structs->next;
			free_struct(s);
		}

		*bytes_needed=0;
		DBG("Command consumed\n");
		DBG("process_fifo: returning %d, bytes_needed 0\n", req_size+1);
		return req_size+1; /* all was processed (including terminating \n)*/

}
Esempio n. 23
0
static void
login_reply (int logged_in)
{
    rpc_send (msock, RPC_INT, logged_in ? MC_LOGINOK : MC_INVALID_PASS,
	      RPC_END);
}
Esempio n. 24
0
static void
do_getupdir (void)
{
    rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : "/", RPC_END);
}
Esempio n. 25
0
static void
do_gethome (void)
{
    rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : "/", RPC_END);
}
Esempio n. 26
0
void on_recv_pkg(nw_ses *ses, rpc_pkg *pkg)
{
    rpc_send(ses, pkg);
}
Esempio n. 27
0
static void
send_status (int status, int errno_number)
{
    rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
    errno = 0;
}