From: David Howells Date: Wed, 8 Jan 2014 23:38:07 +0000 (+0000) Subject: Initial code commit now that the VLDB lookup test python script works X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=bec1acb6d3466ece5db74a90a9e1309179d94b42;p=users%2Fdhowells%2Fkafs-utils.git Initial code commit now that the VLDB lookup test python script works The rxgen.pl script takes a C header file, parses it for #defined constants, structures declarations and synchronous RPC call function declarations and emits the following: - C functions to marshal/unmarshal structs. - C functions to marshal func arguments and dispatch the request. - C functions to unmarshal the replies. - C functions to provide synchronous RPC calls matching the source. - A python module containing: - Python types to wrap the structs. - Python methods to wrap the synchronous RPC calls. - Python constants to wrap the constants. It cannot yet handle functions that deal in bulk types/lists of structs and functions that take interpolated chunks of arbitrary data. It also does not yet emit server-side parts or top layer of the asynchronous client side stuff. It requires python v3 and requires the AF_RXRPC socket family to be available in the kernel. A script called "vl-test.py" is included that can be used to test the functionality. Run without arguments, it will attempt to retrieve the "afs.root" volume from the grand.central.org AFS cell. The test script uses the DNS so may require a python3-dns package installing. Signed-off-by: David Howells --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eb31107 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CFLAGS := $(shell python3-config --cflags) + + +pykafs.so: afs_xg.c afs_py.c afs_py.h + python3 compile_pykafs.py build + +afs_xg.c afs_py.c afs_py.h: afs_xg.h rxgen.pl + ./rxgen.pl afs_xg.h + +clean: + find \( -name "*~" -o -name "*.o" -o -name "*.so" \) -delete + rm -rf build/ + rm -f afs_xg.c afs_py.c afs_py.h diff --git a/af_rxrpc.c b/af_rxrpc.c new file mode 100644 index 0000000..ca5fa6a --- /dev/null +++ b/af_rxrpc.c @@ -0,0 +1,531 @@ +/* AF_RXRPC driver + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include +#include +#include "af_rxrpc.h" +#include "rxgen.h" + +/* + * dump the control messages + */ +static __attribute__((unused)) +void dump_cmsg(struct msghdr *msg) +{ + struct cmsghdr *cmsg; + unsigned long user_id; + unsigned char *p; + int abort_code; + int n; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg)); + p = CMSG_DATA(cmsg); + + printf("CMSG: %zu: ", cmsg->cmsg_len); + + if (cmsg->cmsg_level == SOL_RXRPC) { + switch (cmsg->cmsg_type) { + case RXRPC_USER_CALL_ID: + printf("RXRPC_USER_CALL_ID: "); + if (n != sizeof(user_id)) + goto dump_data; + memcpy(&user_id, p, sizeof(user_id)); + printf("%lx\n", user_id); + continue; + + case RXRPC_ABORT: + printf("RXRPC_ABORT: "); + if (n != sizeof(abort_code)) + goto dump_data; + memcpy(&abort_code, p, sizeof(abort_code)); + printf("%d\n", abort_code); + continue; + + case RXRPC_ACK: + printf("RXRPC_ACK"); + if (n != 0) + goto dump_data_colon; + goto print_nl; + + case RXRPC_RESPONSE: + printf("RXRPC_RESPONSE"); + if (n != 0) + goto dump_data_colon; + goto print_nl; + + case RXRPC_NET_ERROR: + printf("RXRPC_NET_ERROR: "); + if (n != sizeof(abort_code)) + goto dump_data; + memcpy(&abort_code, p, sizeof(abort_code)); + printf("%s\n", strerror(abort_code)); + continue; + + case RXRPC_BUSY: + printf("RXRPC_BUSY"); + if (n != 0) + goto dump_data_colon; + goto print_nl; + + case RXRPC_LOCAL_ERROR: + printf("RXRPC_LOCAL_ERROR: "); + if (n != sizeof(abort_code)) + goto dump_data; + memcpy(&abort_code, p, sizeof(abort_code)); + printf("%s\n", strerror(abort_code)); + continue; + + default: + break; + } + } + + printf("l=%d t=%d", cmsg->cmsg_level, cmsg->cmsg_type); + + dump_data_colon: + printf(": "); + dump_data: + printf("{"); + for (; n > 0; n--, p++) + printf("%02x", *p); + + print_nl: + printf("}\n"); + } +} + +/* + * Open a transport endpoint. + */ +static int open_af_rxrpc_endpoint(int local_port, unsigned short service, int exclusive) +{ + struct sockaddr_rxrpc srx; + int fd, ret; + + fd = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); + if (fd < 0) + return -1; + + if (exclusive) { + ret = setsockopt(fd, SOL_RXRPC, RXRPC_EXCLUSIVE_CONNECTION, + NULL, 0); + if (ret == -1) + return -1; + } + + /* Bind an address to the local endpoint */ + srx.srx_family = AF_RXRPC; + srx.srx_service = service; + srx.transport_type = SOCK_DGRAM; + srx.transport_len = sizeof(srx.transport.sin); + srx.transport.sin.sin_family = AF_INET; + srx.transport.sin.sin_port = htons(local_port); + memset(&srx.transport.sin.sin_addr, 0, 4); + + ret = bind(fd, (struct sockaddr *)&srx, sizeof(srx)); + if (ret < 0) + return -1; + + return fd; +} + +/* + * Make a simple synchronous call. + */ +int rxrpc_simple_sync_call(struct sockaddr_rxrpc *srx, + int local_port, int exclusive, + const void *request, size_t reqlen, + void *reply, size_t replen, + uint32_t *_abort_code) +{ + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov[2]; + size_t ctrllen, received; + unsigned char control[128], overrun[sizeof(long)]; + void *preply; + int endpointfd; + int ret, got_eor; + + endpointfd = open_af_rxrpc_endpoint(local_port, 0, exclusive); + if (endpointfd < 0) + return -1; + + srx->srx_family = AF_RXRPC; + srx->transport_type = SOCK_DGRAM; + + /* request an operation */ + ctrllen = 0; + RXRPC_ADD_CALLID(control, ctrllen, 0x12345); + + iov[0].iov_base = (void *)request; + iov[0].iov_len = reqlen; + + msg.msg_name = srx; + msg.msg_namelen = sizeof(*srx); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = ctrllen; + msg.msg_flags = 0; + + //dump_cmsg(&msg); + + ret = sendmsg(endpointfd, &msg, 0); + if (ret == -1) { + close(endpointfd); + return -1; + } + + /* wait for a reply */ + preply = reply; + received = 0; + got_eor = 0; + while (!got_eor) { + iov[0].iov_base = preply; + iov[0].iov_len = replen - received; + iov[1].iov_base = &overrun; + iov[1].iov_len = sizeof(overrun); + + if (replen - received > 0) { + msg.msg_iov = iov; + msg.msg_iovlen = 2; + } else { + msg.msg_iov = iov + 1; + msg.msg_iovlen = 1; + } + + msg.msg_name = srx; + msg.msg_namelen = sizeof(*srx); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + msg.msg_flags = 0; + + ret = recvmsg(endpointfd, &msg, 0); + if (ret == -1) + return -1; + + //printf("RECV: %d [fl:%d]\n", ret, msg.msg_flags); + //printf("CMSG: %zu\n", msg.msg_controllen); + //printf("IOV: %zu [0]=%zu\n", msg.msg_iovlen, iov[0].iov_len); + + if (msg.msg_flags & MSG_EOR) + got_eor = 1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + unsigned char *p; + int n; + + if (cmsg->cmsg_level != SOL_RXRPC) + continue; + + n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg)); + p = CMSG_DATA(cmsg); + + switch (cmsg->cmsg_type) { + case RXRPC_ABORT: + if (n != sizeof(*_abort_code)) + goto internal_error; + memcpy(_abort_code, p, sizeof(*_abort_code)); + ret = ECONNABORTED; + goto error; + + case RXRPC_NET_ERROR: + case RXRPC_LOCAL_ERROR: + if (n != sizeof(ret)) + goto internal_error; + memcpy(&ret, p, sizeof(ret)); + goto error; + + case RXRPC_BUSY: + ret = ECONNREFUSED; + goto error; + + default: + break; + } + } + + if (ret > replen - received) { + ret = EMSGSIZE; + goto error; + } + + preply += ret; + received += ret; + } + + close(endpointfd); + return received; + +internal_error: + ret = EBADMSG; +error: + errno = ret; + close(endpointfd); + return -1; +} + +/* + * Set up a new connection + */ +struct rx_connection *rx_new_connection(const struct sockaddr *sa, + socklen_t salen, + uint16_t service, + uint16_t local_port, + uint16_t local_service, + int exclusive) +{ + struct sockaddr_rxrpc srx; + struct rx_connection *z_conn; + int ret; + + z_conn = calloc(1, sizeof(*z_conn)); + if (!z_conn) + return NULL; + + z_conn->peer.srx_family = AF_RXRPC; + z_conn->peer.srx_service = service; + z_conn->peer.transport_type = SOCK_DGRAM; + z_conn->peer.transport_len = sizeof(srx.transport.sin); + + switch (sa->sa_family) { + case 0: + errno = EDESTADDRREQ; + goto error_conn; + case AF_INET: + if (salen != sizeof(struct sockaddr_in)) + goto inval; + break; + case AF_INET6: + if (salen != sizeof(struct sockaddr_in6)) + goto inval; + break; + default: + errno = EPROTOTYPE; + goto error_conn; + } + + memcpy(&z_conn->peer.transport, sa, salen); + switch (sa->sa_family) { + case AF_INET: + if (!z_conn->peer.transport.sin.sin_port) { + errno = EDESTADDRREQ; + goto error_conn; + } + break; + case AF_INET6: + if (!z_conn->peer.transport.sin6.sin6_port) { + errno = EDESTADDRREQ; + goto error_conn; + } + break; + } + + /* Open up a socket for talking to the AF_RXRPC module */ + z_conn->fd = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); + if (z_conn->fd < 0) + goto error_conn; + + if (exclusive) { + ret = setsockopt(z_conn->fd, + SOL_RXRPC, RXRPC_EXCLUSIVE_CONNECTION, + NULL, 0); + if (ret == -1) + goto error_conn; + } + + /* Bind an address to the local endpoint */ + memset(&srx, 0, sizeof(srx)); + srx.srx_family = AF_RXRPC; + srx.srx_service = local_service; + srx.transport_type = SOCK_DGRAM; + srx.transport_len = salen; + srx.transport.sin.sin_family = sa->sa_family; + switch (sa->sa_family) { + case AF_INET: + srx.transport.sin.sin_port = htons(local_port); + break; + case AF_INET6: + srx.transport.sin6.sin6_port = htons(local_port); + break; + } + + ret = bind(z_conn->fd, (struct sockaddr *)&srx, sizeof(srx)); + if (ret < 0) + goto error_fd; + + return z_conn; + +inval: + errno = EINVAL; + goto error_conn; +error_fd: + close(z_conn->fd); +error_conn: + free(z_conn); + return NULL; +} + +/* + * Close an RxRPC client connection. This will cause all outstanding + * operations to be aborted by the kernel.. + */ +void rx_close_connection(struct rx_connection *z_conn) +{ + close(z_conn->fd); + free(z_conn); +} + +/* + * Send a request off to the service. + */ +int rxrpc_send_request(struct rx_connection *z_conn, + struct rx_call *call, + const net_xdr_t *request, + size_t request_size, + size_t reply_buf_size) +{ + struct msghdr msg; + struct iovec iov[2]; + size_t ctrllen; + unsigned char control[128]; + + call->got_eor = 0; + call->error_code = 0; + call->abort_code = 0; + call->reply_buf_size = reply_buf_size; + call->reply_size = 0; + + /* request an operation */ + ctrllen = 0; + RXRPC_ADD_CALLID(control, ctrllen, (unsigned long)call); + + iov[0].iov_base = (void *)request; + iov[0].iov_len = request_size; + + msg.msg_name = &z_conn->peer; + msg.msg_namelen = sizeof(z_conn->peer); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = ctrllen; + msg.msg_flags = 0; + + //dump_cmsg(&msg); + + return sendmsg(z_conn->fd, &msg, 0) == -1 ? -1 : 0; +} + +/* + * Wait for a reply to arrive from a single synchronous RPC request. + */ +int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn, + struct rx_call *call) +{ + struct sockaddr_rxrpc srx; + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov[2]; + size_t space; + unsigned char control[128], overrun[sizeof(long)]; + int ret; + + /* wait for a reply */ + while (!call->got_eor) { + space = call->reply_buf_size - call->reply_size; + iov[0].iov_base = call->reply + call->reply_size; + iov[0].iov_len = space; + iov[1].iov_base = &overrun; + iov[1].iov_len = sizeof(overrun); + + if (iov[0].iov_len > 0) { + msg.msg_iov = iov; + msg.msg_iovlen = 2; + } else { + msg.msg_iov = iov + 1; + msg.msg_iovlen = 1; + } + + memcpy(&srx, &z_conn->peer, sizeof(struct sockaddr_rxrpc)); + msg.msg_name = &srx; + msg.msg_namelen = sizeof(srx); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + msg.msg_flags = 0; + + ret = recvmsg(z_conn->fd, &msg, 0); + if (ret == -1) + return -1; + + //printf("RECV: %d [fl:%d]\n", ret, msg.msg_flags); + //printf("CMSG: %zu\n", msg.msg_controllen); + //printf("IOV: %zu [0]=%zu\n", msg.msg_iovlen, iov[0].iov_len); + + if (msg.msg_flags & MSG_EOR) + call->got_eor = 1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + unsigned char *p; + int n; + + if (cmsg->cmsg_level != SOL_RXRPC) + continue; + + n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg)); + p = CMSG_DATA(cmsg); + + switch (cmsg->cmsg_type) { + case RXRPC_ABORT: + if (n != sizeof(call->abort_code)) + call->abort_code = 0; + else + memcpy(&call->abort_code, p, + sizeof(call->abort_code)); + z_conn->last_abort_code = call->abort_code; + errno = ECONNABORTED; + return call->abort_code;; + + case RXRPC_NET_ERROR: + case RXRPC_LOCAL_ERROR: + if (n != sizeof(ret)) { + errno = EBADMSG; + return -1; + } + memcpy(&ret, p, sizeof(ret)); + errno = ret; + return -1; + + case RXRPC_BUSY: + errno = ECONNREFUSED; + return -1; + + default: + break; + } + } + + if (ret > space) { + errno = EMSGSIZE; + return -1; + } + + call->reply_size += ret; + } + + return 0; +} diff --git a/af_rxrpc.h b/af_rxrpc.h new file mode 100644 index 0000000..80041fb --- /dev/null +++ b/af_rxrpc.h @@ -0,0 +1,121 @@ +/* AF_RXRPC definitions + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef AF_RXRPC_H +#define AF_RXRPC_H + +#include +#include + +/* + * Rx kerberos security abort codes + * - unfortunately we have no generalised security abort codes to say things + * like "unsupported security", so we have to use these instead and hope the + * other side understands + */ +#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */ +#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */ +#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */ +#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */ +#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */ +#define RXKADNOAUTH 19270405 /* caller not authorised */ +#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */ +#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */ +#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */ +#define RXKADEXPIRED 19270409 /* authentication expired */ +#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */ +#define RXKADDATALEN 19270411 /* user data too long */ +#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ + +/* + * AF_RXRPC socket address type. + */ +struct sockaddr_rxrpc { + sa_family_t srx_family; /* address family (AF_RXRPC) */ + unsigned short srx_service; /* service desired */ + unsigned short transport_type; /* type of transport socket (SOCK_DGRAM) */ + unsigned short transport_len; /* length of transport address */ + union { + sa_family_t family; /* transport address family */ + struct sockaddr_in sin; /* IPv4 transport address */ + struct sockaddr_in6 sin6; /* IPv6 transport address */ + } transport; +}; + +/* cmsg_level value and sockopt level */ +#define SOL_RXRPC 272 + +/* cmsg_type values */ +#define RXRPC_USER_CALL_ID 1 /* User call ID specifier */ +#define RXRPC_ABORT 2 /* Abort request / notification */ +#define RXRPC_ACK 3 /* [Server] RPC op final ACK received */ +#define RXRPC_RESPONSE 4 /* [Server] security response received */ +#define RXRPC_NET_ERROR 5 /* network error received */ +#define RXRPC_BUSY 6 /* server busy received */ +#define RXRPC_LOCAL_ERROR 7 /* local error generated */ +#define RXRPC_PREPARE_CALL_SLOT 8 /* Propose user call ID specifier for next call */ + +/* sockopt optname */ +#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */ +#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */ +#define RXRPC_EXCLUSIVE_CONNECTION 3 /* [clnt] use exclusive RxRPC connection */ +#define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */ + +/* RXRPC_MIN_SECURITY_LEVEL sockopt values */ +#define RXRPC_SECURITY_PLAIN 0 /* plain secure-checksummed packets only */ +#define RXRPC_SECURITY_AUTH 1 /* authenticated packets */ +#define RXRPC_SECURITY_ENCRYPT 2 /* encrypted packets */ + +/* + * Add a call ID to a control message sequence. + */ +#define RXRPC_ADD_CALLID(control, ctrllen, id) \ +do { \ + void *__buffer = (control); \ + unsigned long *__data; \ + struct cmsghdr *__cmsg; \ + __cmsg = __buffer + (ctrllen); \ + __cmsg->cmsg_len = CMSG_LEN(sizeof(*__data)); \ + __cmsg->cmsg_level = SOL_RXRPC; \ + __cmsg->cmsg_type = RXRPC_USER_CALL_ID; \ + __data = (void *)CMSG_DATA(__cmsg); \ + *__data = (id); \ + (ctrllen) += __cmsg->cmsg_len; \ + \ +} while (0) + +/* + * Add an abort instruction to a control message sequence. + */ +#define RXRPC_ADD_ABORT(control, ctrllen, abort_code) \ +do { \ + void *__buffer = (control); \ + unsigned int *__data; \ + struct cmsghdr *__cmsg; \ + __cmsg = (void *)(control) + (ctrllen); \ + __cmsg->cmsg_len = CMSG_LEN(sizeof((__data)); \ + __cmsg->cmsg_level = SOL_RXRPC; \ + __cmsg->cmsg_type = RXRPC_ABORT; \ + __data = (void *)CMSG_DATA(__cmsg); \ + *__data = (abort_code); \ + (ctrllen) += __cmsg->cmsg_len; \ + \ +} while (0) + +/* + * af_rxrpc.c + */ +extern int rxrpc_simple_sync_call(struct sockaddr_rxrpc *srx, + int local_port, int exclusive, + const void *request, size_t reqlen, + void *reply, size_t replen, + uint32_t *_abort_code); +#endif /* AF_RXRPC_H */ diff --git a/afs.h b/afs.h new file mode 100644 index 0000000..1983f51 --- /dev/null +++ b/afs.h @@ -0,0 +1,170 @@ +/* AFS common types + * + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef AFS_H +#define AFS_H + +#include + +#define AFS_MAXCELLNAME 64 /* maximum length of a cell name */ +#define AFS_MAXVOLNAME 64 /* maximum length of a volume name */ +#define AFSNAMEMAX 256 /* maximum length of a filename plus NUL */ +#define AFSPATHMAX 1024 /* maximum length of a pathname plus NUL */ +#define AFSOPAQUEMAX 1024 /* maximum length of an opaque field */ + +typedef uint32_t afs_volid_t; +typedef uint32_t afs_vnodeid_t; +typedef uint64_t afs_dataversion_t; + +typedef enum { + AFSVL_RWVOL, /* read/write volume */ + AFSVL_ROVOL, /* read-only volume */ + AFSVL_BACKVOL, /* backup volume */ +} __attribute__((packed)) afs_voltype_t; + +typedef enum { + AFS_FTYPE_INVALID = 0, + AFS_FTYPE_FILE = 1, + AFS_FTYPE_DIR = 2, + AFS_FTYPE_SYMLINK = 3, +} afs_file_type_t; + +typedef enum { + AFS_LOCK_READ = 0, /* read lock request */ + AFS_LOCK_WRITE = 1, /* write lock request */ +} afs_lock_type_t; + +#define AFS_LOCKWAIT (5 * 60) /* time until a lock times out (seconds) */ + +/* + * AFS file identifier + */ +struct afs_fid { + afs_volid_t vid; /* volume ID */ + afs_vnodeid_t vnode; /* file index within volume */ + unsigned unique; /* unique ID number (file index version) */ +}; + +/* + * AFS callback notification + */ +typedef enum { + AFSCM_CB_UNTYPED = 0, /* no type set on CB break */ + AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */ + AFSCM_CB_SHARED = 2, /* CB shared by other CM's */ + AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */ +} afs_callback_type_t; + +struct afs_callback { + struct afs_fid fid; /* file identifier */ + unsigned version; /* callback version */ + unsigned expiry; /* time at which expires */ + afs_callback_type_t type; /* type of callback */ +}; + +#define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */ + +/* + * AFS volume information + */ +struct afs_volume_info { + afs_volid_t vid; /* volume ID */ + afs_voltype_t type; /* type of this volume */ + afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */ + + /* list of fileservers serving this volume */ + size_t nservers; /* number of entries used in servers[] */ + struct { + struct in_addr addr; /* fileserver address */ + } servers[8]; +}; + +/* + * AFS security ACE access mask + */ +typedef unsigned afs_access_t; +#define AFS_ACE_READ 0x00000001U /* - permission to read a file/dir */ +#define AFS_ACE_WRITE 0x00000002U /* - permission to write/chmod a file */ +#define AFS_ACE_INSERT 0x00000004U /* - permission to create dirent in a dir */ +#define AFS_ACE_LOOKUP 0x00000008U /* - permission to lookup a file/dir in a dir */ +#define AFS_ACE_DELETE 0x00000010U /* - permission to delete a dirent from a dir */ +#define AFS_ACE_LOCK 0x00000020U /* - permission to lock a file */ +#define AFS_ACE_ADMINISTER 0x00000040U /* - permission to change ACL */ +#define AFS_ACE_USER_A 0x01000000U /* - 'A' user-defined permission */ +#define AFS_ACE_USER_B 0x02000000U /* - 'B' user-defined permission */ +#define AFS_ACE_USER_C 0x04000000U /* - 'C' user-defined permission */ +#define AFS_ACE_USER_D 0x08000000U /* - 'D' user-defined permission */ +#define AFS_ACE_USER_E 0x10000000U /* - 'E' user-defined permission */ +#define AFS_ACE_USER_F 0x20000000U /* - 'F' user-defined permission */ +#define AFS_ACE_USER_G 0x40000000U /* - 'G' user-defined permission */ +#define AFS_ACE_USER_H 0x80000000U /* - 'H' user-defined permission */ + +/* + * AFS file status information + */ +struct afs_file_status { + unsigned if_version; /* interface version */ +#define AFS_FSTATUS_VERSION 1 + + afs_file_type_t type; /* file type */ + unsigned nlink; /* link count */ + uint64_t size; /* file size */ + afs_dataversion_t data_version; /* current data version */ + uint32_t author; /* author ID */ + uint32_t owner; /* owner ID */ + uint32_t group; /* group ID */ + afs_access_t caller_access; /* access rights for authenticated caller */ + afs_access_t anon_access; /* access rights for unauthenticated caller */ + uint32_t mode; /* UNIX mode */ + struct afs_fid parent; /* parent dir ID for non-dirs only */ + time_t mtime_client; /* last time client changed data */ + time_t mtime_server; /* last time server changed data */ + int32_t lock_count; /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */ +}; + +/* + * AFS file status change request + */ + +#define AFS_SET_MTIME 0x01 /* set the mtime */ +#define AFS_SET_OWNER 0x02 /* set the owner ID */ +#define AFS_SET_GROUP 0x04 /* set the group ID (unsupported?) */ +#define AFS_SET_MODE 0x08 /* set the UNIX mode */ +#define AFS_SET_SEG_SIZE 0x10 /* set the segment size (unsupported) */ + +/* + * AFS volume synchronisation information + */ +struct afs_volsync { + time_t creation; /* volume creation time */ +}; + +/* + * AFS volume status record + */ +struct afs_volume_status { + uint32_t vid; /* volume ID */ + uint32_t parent_id; /* parent volume ID */ + uint8_t online; /* true if volume currently online and available */ + uint8_t in_service; /* true if volume currently in service */ + uint8_t blessed; /* same as in_service */ + uint8_t needs_salvage; /* true if consistency checking required */ + uint32_t type; /* volume type (afs_voltype_t) */ + uint32_t min_quota; /* minimum space set aside (blocks) */ + uint32_t max_quota; /* maximum space this volume may occupy (blocks) */ + uint32_t blocks_in_use; /* space this volume currently occupies (blocks) */ + uint32_t part_blocks_avail; /* space available in volume's partition */ + uint32_t part_max_blocks; /* size of volume's partition */ +}; + +#define AFS_BLOCK_SIZE 1024 + +#endif /* AFS_H */ diff --git a/afs_cm.h b/afs_cm.h new file mode 100644 index 0000000..be31a37 --- /dev/null +++ b/afs_cm.h @@ -0,0 +1,33 @@ +/* AFS Cache Manager definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef AFS_CM_H +#define AFS_CM_H + +#define AFS_CM_PORT 7001 /* AFS file server port */ +#define CM_SERVICE 1 /* AFS File Service ID */ + +enum AFS_CM_Operations { + CBCallBack = 204, /* break callback promises */ + CBInitCallBackState = 205, /* initialise callback state */ + CBProbe = 206, /* probe client */ + CBGetLock = 207, /* get contents of CM lock table */ + CBGetCE = 208, /* get cache file description */ + CBGetXStatsVersion = 209, /* get version of extended statistics */ + CBGetXStats = 210, /* get contents of extended statistics data */ + CBInitCallBackState3 = 213, /* initialise callback state, version 3 */ + CBProbeUuid = 214, /* check the client hasn't rebooted */ + CBTellMeAboutYourself = 65538, /* get client capabilities */ +}; + +#define AFS_CAP_ERROR_TRANSLATION 0x1 + +#endif /* AFS_CM_H */ diff --git a/afs_fs.h b/afs_fs.h new file mode 100644 index 0000000..eb64732 --- /dev/null +++ b/afs_fs.h @@ -0,0 +1,56 @@ +/* AFS File Service definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef AFS_FS_H +#define AFS_FS_H + +#define AFS_FS_PORT 7000 /* AFS file server port */ +#define FS_SERVICE 1 /* AFS File Service ID */ + +enum AFS_FS_Operations { + FSFETCHDATA = 130, /* AFS Fetch file data */ + FSFETCHSTATUS = 132, /* AFS Fetch file status */ + FSSTOREDATA = 133, /* AFS Store file data */ + FSSTORESTATUS = 135, /* AFS Store file status */ + FSREMOVEFILE = 136, /* AFS Remove a file */ + FSCREATEFILE = 137, /* AFS Create a file */ + FSRENAME = 138, /* AFS Rename or move a file or directory */ + FSSYMLINK = 139, /* AFS Create a symbolic link */ + FSLINK = 140, /* AFS Create a hard link */ + FSMAKEDIR = 141, /* AFS Create a directory */ + FSREMOVEDIR = 142, /* AFS Remove a directory */ + FSGIVEUPCALLBACKS = 147, /* AFS Discard callback promises */ + FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */ + FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */ + FSGETROOTVOLUME = 151, /* AFS Get root volume name */ + FSSETLOCK = 156, /* AFS Request a file lock */ + FSEXTENDLOCK = 157, /* AFS Extend a file lock */ + FSRELEASELOCK = 158, /* AFS Release a file lock */ + FSLOOKUP = 161, /* AFS lookup file in directory */ + FSFETCHDATA64 = 65537, /* AFS Fetch file data */ + FSSTOREDATA64 = 65538, /* AFS Store file data */ +}; + +enum AFS_FS_Errors { + VSALVAGE = 101, /* volume needs salvaging */ + VNOVNODE = 102, /* no such file/dir (vnode) */ + VNOVOL = 103, /* no such volume or volume unavailable */ + VVOLEXISTS = 104, /* volume name already exists */ + VNOSERVICE = 105, /* volume not currently in service */ + VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */ + VONLINE = 107, /* volume is already online */ + VDISKFULL = 108, /* disk partition is full */ + VOVERQUOTA = 109, /* volume's maximum quota exceeded */ + VBUSY = 110, /* volume is temporarily unavailable */ + VMOVED = 111, /* volume moved to new server - ask this FS where */ +}; + +#endif /* AFS_FS_H */ diff --git a/afs_ka.h b/afs_ka.h new file mode 100644 index 0000000..03a8a6e --- /dev/null +++ b/afs_ka.h @@ -0,0 +1,37 @@ +/* AFS Authentication Service client interface + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef AFS_KA_H +#define AFS_KA_H + +#include "afs.h" + +#define AFS_KA_PORT 7004 /* Authentication service port */ +#define KA_SERVICE 731 /* RxRPC service ID */ +#define KA_TICKET_GRANTING_SERVICE 732 + +enum AFSVL_Operations { + KAA_Authenticate = 21, + KAA_AuthenticateV2 = 22, + KAT_GetToken = 23, +}; + +/* + * rpc_authentication.c + */ +extern int afs_KA_call(struct sockaddr_rxrpc *srx, + const void *request, size_t reqlen, void *reply, size_t replen, + uint32_t *_abort_code); +extern int afs_KAT_call(struct sockaddr_rxrpc *srx, + const void *request, size_t reqlen, void *reply, size_t replen, + uint32_t *_abort_code); + +#endif /* AFS_KA_H */ diff --git a/afs_vl.h b/afs_vl.h new file mode 100644 index 0000000..2eb1097 --- /dev/null +++ b/afs_vl.h @@ -0,0 +1,99 @@ +/* AFS Volume Location Service client interface + * + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef AFS_VL_H +#define AFS_VL_H + +#include "afs.h" + +#define AFS_VL_PORT 7003 /* volume location service port */ +#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */ + +enum AFSVL_Operations { + VLCREATEENTRY = 501, /* AFS Create VLDB entry */ + VLDELETEENTRY = 502, /* AFS Delete VLDB entry */ + VLGETENTRYBYID = 503, /* AFS Get Cache Entry By ID operation ID */ + VLGETENTRYBYNAME = 504, /* AFS Get Cache Entry By Name operation ID */ + VLPROBE = 514, /* AFS Probe Volume Location Service operation ID */ +}; + +enum AFSVL_Errors { + AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */ + AFSVL_IO = 363521, /* I/O related error */ + AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */ + AFSVL_CREATEFAIL = 363523, /* Internal creation failure */ + AFSVL_NOENT = 363524, /* No such entry */ + AFSVL_EMPTY = 363525, /* Vl database is empty */ + AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */ + AFSVL_BADNAME = 363527, /* Volume name is illegal */ + AFSVL_BADINDEX = 363528, /* Index is out of range */ + AFSVL_BADVOLTYPE = 363529, /* Bad volume type */ + AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */ + AFSVL_BADPARTITION = 363531, /* Bad partition number */ + AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */ + AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */ + AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */ + AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */ + AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */ + AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */ + AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */ + AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */ + AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */ + AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */ + AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */ + AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */ + AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */ + AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server flag */ + AFSVL_PERM = 363546, /* No permission access */ + AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */ +}; + +/* + * maps to "struct vldbentry" in vvl-spec.pdf + */ +struct afs_vldbentry { + char name[AFS_MAXVOLNAME + 1]; /* name of volume (with NUL char) */ + afs_voltype_t type; /* volume type */ + unsigned num_servers; /* num servers that hold instances of this vol */ + unsigned clone_id; /* cloning ID */ + + unsigned flags; +#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */ +#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */ +#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */ + + afs_volid_t volume_ids[3]; /* volume IDs */ + + struct { + struct in_addr addr; /* server address */ + unsigned partition; /* partition ID on this server */ + unsigned flags; /* server specific flags */ +#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */ +#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */ +#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */ +#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */ + } servers[8]; +}; + +/* + * rpc_vlocation.c + */ +struct sockaddr_rxrpc; +extern int afs_VL_call(struct sockaddr *sa, socklen_t salen, + const void *request, size_t reqlen, void *reply, size_t replen, + uint32_t *_abort_code); +extern int afs_VL_abort_to_error(uint32_t abort_code); +extern int afs_VL_get_entry_by_name(struct sockaddr *sa, socklen_t salen, + const char *volname, + struct afs_vldbentry *entry, + uint32_t *_abort_code); + +#endif /* AFS_VL_H */ diff --git a/afs_xg.h b/afs_xg.h new file mode 100644 index 0000000..bd6b1b4 --- /dev/null +++ b/afs_xg.h @@ -0,0 +1,155 @@ +/* AFS-3 RPC Interface definitions. + * + * The contents of this file are processed by a script to produce the interface + * routines. + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _AFS_XG_H +#define _AFS_XG_H + +#include "rxgen.h" + +#define AFS_VL_PORT 7003 /* volume location service port */ +#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */ +#define AFS_CM_PORT 7001 /* AFS file server port */ +#define CM_SERVICE 1 /* AFS File Service ID */ + +#define AFS_MAXCELLNAME 64 /* maximum length of a cell name */ +#define AFS_MAXVOLNAME 64 /* maximum length of a volume name */ +#define AFSNAMEMAX 256 /* maximum length of a filename plus NUL */ +#define AFSPATHMAX 1024 /* maximum length of a pathname plus NUL */ +#define AFSOPAQUEMAX 1024 /* maximum length of an opaque field */ + +#define MAXTYPES 3 +#define MAXNSERVERS 8 +#define MAXNAMELEN 65 + +enum AFSVL_Operations { + VLCREATEENTRY = 501, /* AFS Create VLDB entry */ + VLDELETEENTRY = 502, /* AFS Delete VLDB entry */ + VLGETENTRYBYID = 503, /* AFS Get Cache Entry By ID operation ID */ + VLGETENTRYBYNAME = 504, /* AFS Get Cache Entry By Name operation ID */ + VLGETNEWVOLUMEID = 505, /* AFS Generate a new volume ID */ + VLPROBE = 514, /* AFS Probe Volume Location Service operation ID */ +}; + +/* + * AFS Volume Location Database entry. + * + * As defined in AFS-3 Vol/VL Server Spec #3.3.1. + */ +struct vldbentry { + char name[MAXNAMELEN]; /* The volume name */ + uint32_t volumeType; /* The volume type */ + uint32_t nServers; /* The number of servers having an instance */ + uint32_t serverNumber[MAXNSERVERS]; /* Server addresses */ + uint32_t serverPartition[MAXNSERVERS]; /* Partition ID on corresponding servers */ + uint32_t serverFlags[MAXNSERVERS]; /* Flags for corresponding servers */ + uint32_t volumeId[MAXTYPES]; /* Volume ID for each type */ + uint32_t cloneId; /* Cloning operation ID */ + uint32_t flags; /* Flags indicating presence of fields */ +}; + +/* + * AFS Volume Location Database entry update request. + * + * As defined in AFS-3 Vol/VL Server Spec #3.3.5. + */ +struct VldbUpdateEntry { + uint32_t Mask; /* Bit mask indicating fields to be affected */ + char name[MAXNAMELEN]; /* The volume name */ + uint32_t volumeType; /* The volume type */ + uint32_t flags; /* Used in conjunction with Mask to select valid fields */ + uint32_t ReadOnlyId; /* The read-only ID */ + uint32_t BackupId; /* The backup ID */ + uint32_t cloneId; /* The clone ID */ + uint32_t nModifiedRepsites; /* Number of replication site entries to be changed */ + uint32_t RepsitesMask[MAXNSERVERS]; /* Bit mask for each rep site */ + uint32_t RepsitesTargetServer[MAXNSERVERS]; /* Target servers */ + uint32_t RepsitesTargetPart[MAXNSERVERS]; /* Target server partitions */ + uint32_t RepsitesNewServer[MAXNSERVERS]; /* New server sites */ + uint32_t RepsitesNewPart[MAXNSERVERS]; /* New server partitions */ + uint32_t RepsitesNewFlags[MAXNSERVERS]; /* Flags applying to each new site */ +}; + +/* + * AFS VLDB list query by-attribute filter. + * + * As defined in AFS-3 Vol/VL Server Spec #3.3.6. + */ +struct VldbListByAttributes { + uint32_t Mask; /* Bit mask used to select attr fields to match */ + uint32_t server; /* The server address to match */ + uint32_t partition; /* The partition ID to match */ + uint32_t volumetype; /* The volume type to match */ + uint32_t volumeid; /* The volume ID to match */ + uint32_t flag; /* Flags concerning these values */ +}; + +/* + * Create a VLDB entry (AFS-3 Vol/VL Server Spec #3.6.1) + * + * Operation: VLCREATEENTRY + */ +extern int VL_CreateEntry( + struct rx_connection *z_conn, + /*IN*/ const struct vldbentry *newentry); + +/* + * Delete a VLDB entry (AFS-3 Vol/VL Server Spec #3.6.2) + * + * Operation: VLDELETEENTRY + */ +extern int VL_DeleteEntry( + struct rx_connection *z_conn, + /*IN*/ uint32_t Volid, + /*IN*/ uint32_t voltype); + +/* + * Get a VLDB entry by ID/type (AFS-3 Vol/VL Server Spec #3.6.3) + * + * Operation: VLGETENTRYBYID + */ +extern int VL_GetEntryByID( + struct rx_connection *z_conn, + /*IN*/ uint32_t Volid, + /*IN*/ uint32_t voltype, + /*OUT*/ struct vldbentry *entry); + +/* + * Get a VLDB entry by volume name (AFS-3 Vol/VL Server Spec #3.6.4) + * + * Operation: VLGETENTRYBYNAME + */ +extern int VL_GetEntryByName( + struct rx_connection *z_conn, + /*IN*/ const char *volumename, /* Max MAXNAMELEN */ + /*OUT*/ struct vldbentry *entry); + +/* + * Generate a new volume ID (AFS-3 Vol/VL Server Spec #3.6.5) + * + * Operation: VLGETNEWVOLUMEID + */ +extern int VL_GetNewVolumeId( + struct rx_connection *z_conn, + /*IN*/ uint32_t bumpcount, + /*OUT*/ uint32_t *newvolumid); + +/* + * Probe an AFS VL server (AFS-3 Vol/VL Server Spec #3.6.14) + * + * Operation: VLPROBE + */ +extern int VL_Probe( + struct rx_connection *z_conn); + +#endif /* _AFS_XG_H */ diff --git a/afsdns.py b/afsdns.py new file mode 100755 index 0000000..c2d2e07 --- /dev/null +++ b/afsdns.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 + +import dns.resolver; + +vladdrs = []; +answers = dns.resolver.query('grand.central.org', 'AFSDB'); +for record in answers: + print("--", record.hostname, "--"); + A_recs = dns.resolver.query(record.hostname, 'A'); + for addr in A_recs: + vladdrs.append(addr.address); + print(vladdrs); diff --git a/compile_pykafs.py b/compile_pykafs.py new file mode 100644 index 0000000..c5af6ed --- /dev/null +++ b/compile_pykafs.py @@ -0,0 +1,10 @@ +from distutils.core import setup, Extension +setup(name="kafs", version="0.1", + ext_modules=[Extension("kafs", + sources = [ "kafs.c", + "afs_py.c", + "py_rxgen.c", + "afs_xg.c", + "af_rxrpc.c" + ], + )]) diff --git a/kafs.c b/kafs.c new file mode 100644 index 0000000..ef3b3a5 --- /dev/null +++ b/kafs.c @@ -0,0 +1,75 @@ +/* kAFS python module + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "structmember.h" +#include "kafs.h" +#include "afs_py.h" + +#if 0 +/* + * The static methods. + */ +static PyMethodDef module_methods[] = { + {"new_vlocation", (PyCFunction)kafs_new_vlocation, METH_NOARGS, + "Create a new vlocation record." + }, + {"VL_GetEntryByName", (PyCFunction)kafs_VL_GetEntryByName, METH_VARARGS, + "Look up an entry by name." + }, + {NULL} /* Sentinel */ +}; +#endif + +/* + * Initialise the module. + */ +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +PyInit_kafs(void) +{ + PyObject *m; + + m = pykafs_load_wrappers(); + if (!m) { +#if PY_VERSION_HEX >= 0x03000000 + return NULL; +#else + return; +#endif + } + +#if 0 + PyModule_AddIntConstant(m, "RWVOL", AFSVL_RWVOL); + PyModule_AddIntConstant(m, "ROVOL", AFSVL_ROVOL); + PyModule_AddIntConstant(m, "BACKVOL", AFSVL_BACKVOL); + + PyModule_AddIntConstant(m, "RWVOL_EXISTS", AFS_VLF_RWEXISTS); + PyModule_AddIntConstant(m, "ROVOL_EXISTS", AFS_VLF_ROEXISTS); + PyModule_AddIntConstant(m, "BACKVOL_EXISTS", AFS_VLF_BACKEXISTS); + + PyModule_AddIntConstant(m, "NEWREPSITE", AFS_VLSF_NEWREPSITE); + PyModule_AddIntConstant(m, "ROVOL_HERE", AFS_VLSF_ROVOL); + PyModule_AddIntConstant(m, "RWVOL_HERE", AFS_VLSF_RWVOL); + PyModule_AddIntConstant(m, "BACKVOL_HERE", AFS_VLSF_BACKVOL); +#endif + +#if PY_VERSION_HEX >= 0x03000000 + return m; +#else + return; +#endif +} diff --git a/kafs.h b/kafs.h new file mode 100644 index 0000000..25eccae --- /dev/null +++ b/kafs.h @@ -0,0 +1,23 @@ +/* kAFS interface + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _KAFS_H +#define _KAFS_H + +/* + * type_vlocation.c + */ +extern PyTypeObject vlocationType; +extern PyObject *kafs_new_vlocation(PyObject *_self, PyObject *args); +extern PyObject *kafs_VL_GetEntryByName(PyObject *_self, PyObject *args); + + +#endif /* _KAFS_H */ diff --git a/py_rxgen.c b/py_rxgen.c new file mode 100644 index 0000000..8f94e6a --- /dev/null +++ b/py_rxgen.c @@ -0,0 +1,268 @@ +/* rxgen Python wrapping support + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include "structmember.h" +#include +#include "py_rxgen.h" +#include "rxgen.h" + +PyObject *py_rxgen_get_string(const void *_array, size_t n) +{ + const char *array = _array; + return PyUnicode_FromStringAndSize(array, strnlen(array, n - 1)); +} + +int py_rxgen_set_string(void *_array, size_t n, PyObject *val) +{ + Py_ssize_t len; + char *array = _array; + char *new_name; + + if (!PyUnicode_Check(val)) + return -1; + + new_name = PyUnicode_AsUTF8AndSize(val, &len); + if (!new_name) + return -1; + + if (len < 0 || len > n - 1) + return -1; + + memcpy(array, new_name, len); + memset(array + len, 0, n - len); + return 0; +} + +PyObject *py_rxgen_get_uint8(const void *_array, size_t n) +{ + PyObject *list; + const uint8_t *array = _array; + int i; + + list = PyTuple_New(n); + if (!list) + return NULL; + + for (i = 0; i < n; i++) { + PyObject *num = PyLong_FromLong(array[i]); + if (!num) + goto error; + + if (PyTuple_SetItem(list, i, num) != 0) { + Py_DECREF(num); + goto error; + } + } + return list; + +error: + Py_DECREF(list); + return NULL; +} + +PyObject *py_rxgen_get_uint16(const void *_array, size_t n) +{ + PyObject *list; + const uint16_t *array = _array; + int i; + + list = PyTuple_New(n); + if (!list) + return NULL; + + for (i = 0; i < n; i++) { + PyObject *num = PyLong_FromLong(array[i]); + if (!num) + goto error; + + if (PyTuple_SetItem(list, i, num) != 0) { + Py_DECREF(num); + goto error; + } + } + return list; + +error: + Py_DECREF(list); + return NULL; +} + +PyObject *py_rxgen_get_uint32(const void *_array, size_t n) +{ + PyObject *list; + const uint32_t *array = _array; + int i; + + list = PyTuple_New(n); + if (!list) + return NULL; + + for (i = 0; i < n; i++) { + PyObject *num = PyLong_FromLong(array[i]); + if (!num) + goto error; + + if (PyTuple_SetItem(list, i, num) != 0) { + Py_DECREF(num); + goto error; + } + } + return list; + +error: + Py_DECREF(list); + return NULL; +} + +int py_rxgen_set_uint32(void *_array, size_t n, PyObject *val) +{ + uint32_t *array = _array; + int i; + + if (!PyTuple_Check(val)) { + PyErr_Format(PyExc_TypeError, + "Expected a tuple"); + return -1; + } + if (PyTuple_GET_SIZE(val) != n) { + PyErr_Format(PyExc_ValueError, + "Expected a tuple of %zu size", n); + return -1; + } + + PyErr_Clear(); + for (i = 0; i < n; i++) { + PyObject *p = PyTuple_GET_ITEM(val, i); + unsigned long val = PyLong_AsUnsignedLong(p); + + if (PyErr_Occurred()) + return -1; + array[i] = val; + } + + return 0; +} + +/* + * RxRPC connection container. + */ +static int +py_rx_connection_init(PyObject *_self, PyObject *args, PyObject *kwds) +{ + struct py_rx_connection *self = (struct py_rx_connection *)_self; + self->x = NULL; + return 0; +} + +static void +py_rx_connection_dealloc(struct py_rx_connection *self) +{ + if (self->x) { + rx_close_connection(self->x); + self->x = NULL; + } + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyTypeObject py_rx_connectionType = { + PyVarObject_HEAD_INIT(NULL, 0) + "kafs.rx_connection", /*tp_name*/ + sizeof(struct py_rx_connection), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)py_rx_connection_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "RxRPC connection container", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + py_rx_connection_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +/* + * Set up an RxRPC connection. + */ +PyObject * +kafs_py_rx_new_connection(PyObject *_self, PyObject *args) +{ + struct py_rx_connection *obj; + struct rx_connection *z_conn; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa; + const char *address = NULL; + socklen_t salen; + uint16_t port, service, local_port = 0, local_service = 0; + int exclusive = 0; + + if (!PyArg_ParseTuple(args, "sHH|HHp", + &address, &port, &service, + &local_port, &local_service, &exclusive)) + return NULL; + + memset(&sa, 0, sizeof(sa)); + if (inet_pton(AF_INET, address, &sa.sin.sin_addr)) { + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(port); + salen = sizeof(sa.sin); + } else if (inet_pton(AF_INET6, address, &sa.sin.sin_addr)) { + sa.sin6.sin6_family = AF_INET6; + sa.sin6.sin6_port = htons(port); + salen = sizeof(sa.sin6); + } else { + return PyExc_TypeError; + } + + obj = (struct py_rx_connection *)_PyObject_New(&py_rx_connectionType); + if (!obj) + return PyExc_MemoryError; + py_rx_connection_init((PyObject *)obj, NULL, NULL); + assert(obj->x == NULL); + + z_conn = rx_new_connection(&sa.sa, salen, service, + local_port, local_service, exclusive); + if (!z_conn) { + Py_DECREF(obj); + return errno == ENOMEM ? PyExc_MemoryError : + PyErr_SetFromErrno(PyExc_IOError); + } + obj->x = z_conn; + return (PyObject *)obj; +} diff --git a/py_rxgen.h b/py_rxgen.h new file mode 100644 index 0000000..bbe8392 --- /dev/null +++ b/py_rxgen.h @@ -0,0 +1,34 @@ +/* rxgen Python wrapping support declarations + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _PY_RXGEN_H +#define _PY_RXGEN_H + +struct py_rx_connection { + PyObject_HEAD + struct rx_connection *x; +}; + +extern PyTypeObject py_rx_connectionType; + +extern PyObject *kafs_py_rx_new_connection(PyObject *, PyObject *); + +extern PyObject *py_rxgen_get_string(const void *_p, size_t n); +extern PyObject *py_rxgen_get_uint8(const void *_p, size_t n); +extern PyObject *py_rxgen_get_uint16(const void *_p, size_t n); +extern PyObject *py_rxgen_get_uint32(const void *_p, size_t n); + +extern int py_rxgen_set_string(void *_p, size_t n, PyObject *val); +extern int py_rxgen_set_uint8(void *_p, size_t n, PyObject *val); +extern int py_rxgen_set_uint16(void *_p, size_t n, PyObject *val); +extern int py_rxgen_set_uint32(void *_p, size_t n, PyObject *val); + +#endif /* _PY_RXGEN_H */ diff --git a/rxgen.h b/rxgen.h new file mode 100644 index 0000000..2637dc9 --- /dev/null +++ b/rxgen.h @@ -0,0 +1,52 @@ +/* RxRPC autogen defs + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _RXGEN_H +#define _RXGEN_H + +#include "af_rxrpc.h" + +typedef uint32_t net_xdr_t; + +struct rx_connection { + struct sockaddr_rxrpc peer; + uint32_t last_abort_code; + int fd; +}; + +struct rx_call { + int got_eor; + int error_code; + uint32_t abort_code; + unsigned reply_buf_size; + unsigned reply_size; + net_xdr_t *reply; +}; + +extern struct rx_connection *rx_new_connection(const struct sockaddr *sa, + socklen_t salen, + uint16_t service, + uint16_t local_port, + uint16_t local_service, + int exclusive); + +extern void rx_close_connection(struct rx_connection *z_conn); + +extern int rxrpc_send_request(struct rx_connection *z_conn, + struct rx_call *call, + const net_xdr_t *request, + size_t request_size, + size_t reply_buf_size); + +extern int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn, + struct rx_call *call); + +#endif /* _RXGEN_H */ diff --git a/rxgen.pl b/rxgen.pl new file mode 100755 index 0000000..5106a0b --- /dev/null +++ b/rxgen.pl @@ -0,0 +1,1145 @@ +#!/usr/bin/perl -w +# +# Tool for processing an RxRPC-based RPC API definition in a C header file to +# produce (un)marshalling code and RPC functions to implement that API. +# +# It also produces a python module containing wrappers for the types, RPC +# functions and constants in the API definition. +# +# +# Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# + +use strict; + +sub emit_py_struct_wrapper($@); +sub emit_py_func_simple_sync_call($$$@); + +die "Need list of sources\n" if ($#ARGV < 0); + +my @structs = (); # Structure definitions +my %struct_sizes = (); # Structure sizes +my %funcs = (); # Function declarations +my %constants = (); # #defined constants + +# +# Divide the lines from the files up into typed collections +# +my @comment = (); +my $banner = 0; +my $struct = 0; +my $size = 0; +my $func = 0; +my @members; + +my @files = @ARGV; + +while (my $line = <>) { + # Gather comments for later attachment to subsequent structs and funcs + if ($line =~ m@^/[*]@) { + die if $banner; + $banner = 1; + @comment = ( $line ); + next; + } + if ($banner) { + die if ($line !~ /^ [*]/); + push @comment, $line; + $banner = 0 if ($line =~ m!^ [*]/!); + next; + } + + # Extract #defines + if ($line =~ /^#define\s+([A-Za-z0-9_]+)\s+(.*)/) { + $constants{$1} = $2 if ($2); + @comment = (); + next; + } + + # Extract structures + if ($line =~ /^struct ([a-zA-Z_][a-zA-Z0-9_]*) {/) { + $struct = "$1"; + $size = 0; + @members = ( [ @comment ] ); + next; + } + + if ($line =~ /};/ && $struct) { + push @structs, [ $struct, @members ]; + $struct_sizes{$struct} = $size; + @members = (); + $struct = 0; + next; + } + + # Extract structure members + if ($struct) { + # Strip trailing comments + $line =~ s@\s*/[*][^*]*[*]/$@@; + + if ($line =~ /(uint[0-9]+_t|char)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*;/) { + push @members, [ $1, $2, -1, -1 ]; + $size += 4; + #print "nonarray $2\n"; + } elsif ($line =~ /(uint[0-9]+_t|char)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\[\s*([^]]+)\s*\]\s*;/) { + die "No constant for $1" unless exists $constants{$3}; + my $array_size = $constants{$3}; + push @members, [ $1, $2, $array_size, -1 ]; + $size += $array_size * 4; + #print "array $2\n"; + } else { + die "Unrecognised struct member"; + } + } + + # Extract functions + if ($line =~ /^extern\s+int\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\n/) { + #print "func $1\n"; + $func = "$1"; + @members = ( [ @comment ] ); + next; + } + + # Extract function parameters + if ($func) { + my $dir = ""; + my $const = ""; + my $type = ""; + my $name = ""; + my $term = 0; + my $max = -1; + + chomp $line; + + $dir = $1 if ($line =~ s@/[*](IN|OUT|INOUT)[*]/\s+@@); + + if ($line =~ s@/[*]\s+Max ([0-9]+)\s+[*]/@@) { + $max = $1; + } elsif ($line =~ s@/[*]\s+Max ([a-zA-Z0-9_]+)\s+[*]/@@) { + die "No constant for $1" unless exists $constants{$1}; + $max = $constants{$1}; + } + + # Strip trailing comments + $line =~ s@\s*/[*][^*]*[*]/$@@; + $line =~ s/\s+$//; + + if ($line =~ s/[)];$//) { + $term = 1; + } elsif ($line =~ s/,$//) { + $term = 0; + } else { + die "Unexpected line termination '$line'"; + } + + $line =~ s/\s+$//; + + $const = $1 if ($line =~ s@^const\s+@@); + + #print "\"", $line, "\"\n"; + + if ($line =~ /(struct\s+[a-zA-Z_][a-zA-Z0-9_]*|uint[0-9]+_t|char)\s+([*]*)([a-zA-Z_][a-zA-Z0-9_]*)\s*/) { + $type = $1 . $2; + $name = $3; + + #print "- ", $name, " ISA ", $type, " ", $dir, "\n"; + push @members, [ $type, $name, -1, $max, $dir ] unless ($1 eq "struct rx_connection"); + } else { + die "Unhandled RPC call parameter"; + } + + if ($term) { + $funcs{$func} = [ @members ]; + @members = (); + $func = 0; + next; + } + } + +} + +#print "----------------------------------------\n"; + +#foreach $_ (sort keys %constants) { +# print $_, " -> (", $constants{$_}, ")\n"; +#} + +# foreach $_ (keys %structs) { +# print "-- ", $_, " --\n"; +# my $p = $structs{$_}; +# print $#{$p}, "\n"; +# my @members = @{$p}; +# shift @members; +# foreach my $m (@members) { +# if (@{$m}[2] eq -1) { +# print @{$m}[1], " IS ", @{$m}[0], "\n"; +# } else { +# print @{$m}[1], " IS ", @{$m}[0], "[", @{$m}[2], "]\n"; +# } +# } +# } + +# foreach $_ (keys %funcs) { +# print "-- ", $_, " --\n"; +# my $p = $funcs{$_}; +# my @members = @{$p}; +# shift @members; +# foreach my $m (@members) { +# if (@{$m}[2] eq -1) { +# print "\t", @{$m}[1], " IS ", @{$m}[0], "\n"; +# } else { +# print "\t", @{$m}[1], " IS ", @{$m}[0], "[", @{$m}[2], "]\n"; +# } +# } +# } + +open RXOUT, ">afs_xg.c" || die "afs_xg.c"; +print RXOUT "/* AUTOGENERATED */\n"; +print RXOUT "#define _XOPEN_SOURCE\n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \n"; +print RXOUT "#include \"rxgen.h\"\n"; + +foreach $_ (@files) { + print RXOUT "#include \"$_\"\n"; +} + +open PYHDR, ">afs_py.h" || die "afs_py.h"; +print PYHDR "/* AUTOGENERATED */\n"; +print PYHDR "#include \n"; +print PYHDR "#include \n"; +print PYHDR "#include \"afs_xg.h\"\n"; + +open PYOUT, ">afs_py.c" || die "afs_py.c"; +print PYOUT "/* AUTOGENERATED */\n"; +print PYOUT "#include \n"; +print PYOUT "#include \"structmember.h\"\n"; +print PYOUT "#include \"afs_py.h\"\n"; +print PYOUT "#include \n"; +print PYOUT "#include \"py_rxgen.h\"\n"; + +############################################################################### +# +# Emit structure encoders and decoders +# +############################################################################### +sub emit_struct_encdec ($@) { + my ($struct, @members) = @_; + + # Dump the banner comment block + print RXOUT "\n"; + print RXOUT @{shift @members}; + + # Write an encoding function + print RXOUT "static net_xdr_t *rxgen_encode_", $struct, "(net_xdr_t *xdr, const struct $struct *p)\n"; + print RXOUT "{\n"; + + foreach my $m (@members) { + my ($type, $name, $array_size, $max_size) = @{$m}; + if ($array_size != -1) { + print RXOUT "\tint i;\n\n"; + last; + } + } + + foreach my $m (@members) { + my ($type, $name, $array_size, $max_size) = @{$m}; + if ($array_size != -1) { + print RXOUT "\tfor (i = 0; i < ", $array_size, "; i++)\n"; + print RXOUT "\t\t*xdr++ = htonl(p->", $name, "[i]);\n"; + } else { + print RXOUT "\t*xdr++ = htonl(p->", $name, ");\n"; + } + } + + print RXOUT "\treturn xdr;\n"; + print RXOUT "}\n"; + print RXOUT "\n"; + + # Write a decoding function + print RXOUT "static const net_xdr_t *rxgen_decode_", $struct, "(struct $struct *p, const net_xdr_t *xdr)\n"; + print RXOUT "{\n"; + + foreach my $m (@members) { + my ($type, $name, $array_size, $max_size) = @{$m}; + if ($array_size != -1) { + print RXOUT "\tint i;\n\n"; + last; + } + } + + foreach my $m (@members) { + my ($type, $name, $array_size, $max_size) = @{$m}; + if ($array_size != -1) { + print RXOUT "\tfor (i = 0; i < ", $array_size, "; i++)\n"; + print RXOUT "\t\tp->", $name, "[i] = ntohl(*xdr++);\n"; + } else { + print RXOUT "\tp->", $name, " = ntohl(*xdr++);\n"; + } + } + + print RXOUT "\treturn xdr;\n"; + print RXOUT "}\n"; + print RXOUT "\n"; + print RXOUT "/* XDR size ", $struct_sizes{$struct}, " */\n" +} + +foreach my $s (@structs) { + my @members = @{$s}; + my $struct = shift @members; + emit_struct_encdec($struct, @members); + emit_py_struct_wrapper($struct, @members); +} + +############################################################################### +# +# Emit a function to encode a request +# +############################################################################### +sub emit_func_enc_request($$$$$@) +{ + my ($func, $op, $request_size, $req_has_charptr, $reply_size, @request) = @_; + + # Function definition and arguments + print RXOUT "int rxgen_send_request_", $func, "(\n"; + print RXOUT "\tstruct rx_connection *z_conn,\n"; + print RXOUT "\tstruct rx_call *call"; + foreach my $p (@request) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print RXOUT ",\n\t"; + print RXOUT "const " if ($type =~ "[*]"); + print RXOUT "$type $name"; + die "Array arg not supported '$name'" if ($array_size != -1); + } + print RXOUT ")\n"; + + # Function body, beginning with local variables + print RXOUT "{\n"; + if ($req_has_charptr) { + print RXOUT "\tnet_xdr_t request[", $request_size / 4, " + 1], *xdr;\n"; + print RXOUT "\tuint32_t tmp;\n"; + } else { + print RXOUT "\tnet_xdr_t request[", $request_size / 4, "], *xdr;\n"; + } + + # Marshal the data + print RXOUT "\n"; + print RXOUT "\txdr = request;\n"; + print RXOUT "\t*xdr++ = htonl($op);\n"; + foreach my $p (@request) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($type eq "uint8_t" || + $type eq "uint16_t" || + $type eq "uint32_t") { + print RXOUT "\t*xdr++ = htonl($name);\n"; + } elsif ($type eq "char*") { + print RXOUT "\ttmp = strlen($name);\n"; + print RXOUT "\t*xdr++ = htonl(tmp);\n"; + print RXOUT "\txdr[tmp / 4] = 0;\n"; + print RXOUT "\tmemcpy(xdr, $name, tmp);\n"; + print RXOUT "\txdr += (tmp + 3) / 4;\n"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print RXOUT "\txdr = rxgen_encode_$1(xdr, $name);\n"; + } + } + + # Send the message + print RXOUT "\n"; + print RXOUT "\treturn rxrpc_send_request(z_conn, call, request, (xdr - request) * 4, $reply_size);\n"; + print RXOUT "}\n"; +} + +############################################################################### +# +# Emit a function to decode a reply +# +############################################################################### +sub emit_func_dec_reply($$$@) +{ + my ($func, $op, $reply_size, @reply) = @_; + + print RXOUT "\n"; + + # Function definition and arguments + print RXOUT "void rxgen_decode_reply_", $func, "(\n"; + print RXOUT "\tstruct rx_connection *z_conn,\n"; + print RXOUT "\tstruct rx_call *call"; + foreach my $p (@reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print RXOUT ",\n\t"; + print RXOUT "$type $name"; + die "Array arg not supported '$name'" if ($array_size != -1); + } + print RXOUT ")\n"; + + # Function body, beginning with local variables + print RXOUT "{\n"; + print RXOUT "\tconst net_xdr_t *xdr;\n"; + + # Unmarshal the data + print RXOUT "\n"; + print RXOUT "\txdr = call->reply;\n"; + foreach my $p (@reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($type eq "int8_t*" || + $type eq "int16_t*" || + $type eq "int32_t*" || + $type eq "uint8_t*" || + $type eq "uint16_t*" || + $type eq "uint32_t*") { + print RXOUT "\t*$name = ntohl(*xdr++);\n"; + } elsif ($type eq "int64_t*" || + $type eq "uint64_t*") { + print RXOUT "\t*$name = (uint64_t)ntohl(*xdr++) << 32\n"; + print RXOUT "\t\t | (uint64_t)ntohl(*xdr++);\n"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print RXOUT "\txdr = rxgen_decode_$1($name, xdr);\n"; + } + } + + print RXOUT "}\n"; +} + +############################################################################### +# +# Emit a function to make a simple synchronous call +# +############################################################################### +sub emit_func_simple_sync_call($$$$$@) +{ + my ($func, $request_size, $reply_size, $_request, $_reply, @params) = @_; + my @request = @{$_request}; + my @reply = @{$_reply}; + + print RXOUT "\n"; + + # Function definition and arguments + print RXOUT "int ", $func, "(\n"; + print RXOUT "\tstruct rx_connection *z_conn"; + foreach my $p (@params) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print RXOUT ",\n\t"; + print RXOUT "const " if ($type =~ "[*]" && $dir eq "IN"); + print RXOUT "$type $name"; + die "Array arg not supported '$name'" if ($array_size != -1); + } + print RXOUT ")\n"; + + # Function body, beginning with local variables + print RXOUT "{\n"; + print RXOUT "\tstruct rx_call *call;\n"; + print RXOUT "\tint ret;\n"; + print RXOUT "\n"; + + # Allocate a call record and reply buffer + print RXOUT "\tcall = malloc(sizeof(*call) + $reply_size);\n"; + print RXOUT "\tif (!call)\n"; + print RXOUT "\t\treturn -1;\n"; + print RXOUT "\tcall->reply = (void *)call + sizeof(*call);\n"; + print RXOUT "\n"; + + # Send the request + print RXOUT "\tret = rxgen_send_request_", $func, "("; + print RXOUT "z_conn, call"; + foreach my $p (@request) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print RXOUT ", $name"; + } + print RXOUT ");\n"; + print RXOUT "\tif (ret != 0) {\n"; + print RXOUT "\t\tfree(call);\n"; + print RXOUT "\t\treturn ret;\n"; + print RXOUT "\t}\n"; + + # Wait for the reply + print RXOUT "\tret = rxrpc_wait_for_sync_reply(z_conn, call);\n"; + print RXOUT "\tif (ret != 0) {\n"; + print RXOUT "\t\tfree(call);\n"; + print RXOUT "\t\treturn ret;\n"; + print RXOUT "\t}\n"; + print RXOUT "\n"; + + # Unmarshal the reply + if (@reply) { + print RXOUT "\trxgen_decode_reply_", $func, "("; + print RXOUT "z_conn, call"; + foreach my $p (@reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print RXOUT ", $name"; + } + print RXOUT ");\n"; + } + + print RXOUT "\n"; + print RXOUT "\treturn 0;\n"; + print RXOUT "}\n"; +} + +############################################################################### +# +# Dump RPC call encoders and decoders +# +############################################################################### +foreach $func (sort keys %funcs) { + my @params = @{$funcs{$func}}; + + # Dump the banner comment block + my @banner = @{shift @params}; + print RXOUT "\n"; + print RXOUT @banner; + print PYOUT "\n"; + print PYOUT @banner; + + # Find the Operation ID from the banner comment + my $op = ""; + foreach $_ (@banner) { + if ($_ =~ /Operation: ([A-Z][_A-Z0-9]+)/) { + $op = $1; + last; + } + } + + die "Operation ID unspecified for $func" unless $op; + + # Filter the parameters into request and reply + my @request = (); + my @reply = (); + my $request_size = 4; + my $reply_size = 0; + my $req_has_charptr = 0; + + foreach my $p (@params) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + + #print RXOUT $dir, " ", $type, " ", $name, "\n"; + + my $size = 0; + my $has_charptr = 0; + if ($array_size == -1) { + if ($type eq "char*") { + $size = (4 + $max_size + 3) & ~3; + $has_charptr = 1; + } elsif ($type eq "uint8_t" || $type eq "uint8_t*" || + $type eq "uint16_t" || $type eq "uint16_t*" || + $type eq "uint32_t" || $type eq "uint32_t*" || + $type eq "uint64_t" || $type eq "uint64_t*") { + $size = 4; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + die "No encoding for $type" unless exists $struct_sizes{$1}; + $size = $struct_sizes{$1}; + } else { + die "Unsupported type \"$type\""; + } + } else { + if ($type eq "uint8_t" || $type eq "uint8_t*" || + $type eq "uint16_t" || $type eq "uint16_t*" || + $type eq "uint32_t" || $type eq "uint32_t*" || + $type eq "uint64_t" || $type eq "uint64_t*") { + $size = 4; + } else { + die "Unsupported type \"$type\""; + } + $size *= $array_size; + } + + if ($dir eq "IN") { + push @request, $p; + $request_size += $size; + $req_has_charptr |= $has_charptr; + } elsif ($dir eq "OUT") { + push @reply, $p; + $reply_size += $size; + } elsif ($dir eq "INOUT") { + push @reply, $p; + push @request, $p; + $request_size += $size; + $reply_size += $size; + $req_has_charptr |= $has_charptr; + } + } + + #print RXOUT "/* req max size: $request_size */\n"; + #print RXOUT "/* rep max size: $reply_size */\n"; + emit_func_enc_request($func, $op, $request_size, $req_has_charptr, $reply_size, @request); + emit_func_dec_reply($func, $op, $reply_size, @reply) + if (@reply); + emit_func_simple_sync_call($func, $request_size, $reply_size, \@request, \@reply, @params); + emit_py_func_simple_sync_call($func, \@request, \@reply, @params); +} + +############################################################################### +# +# Emit python structure wrappers +# +############################################################################### +sub emit_py_struct_wrapper($@) { + my ($struct, @members) = @_; + + # Dump the banner comment block + my @comments = @{shift @members}; + + print PYHDR "\n"; + print PYHDR @comments; + print PYOUT "\n"; + print PYOUT @comments; + + # Write a python wrapper struct + print PYHDR "struct py_$struct {\n"; + print PYHDR "\tPyObject_HEAD\n"; + print PYHDR "\tstruct $struct x;\n"; + print PYHDR "};\n"; + print PYHDR "\n"; + + # We want allocation and deallocation functions + print PYOUT "static PyObject *\n"; + print PYOUT "py_", $struct, "_new(PyTypeObject *type, PyObject *args, PyObject *kwds)\n"; + print PYOUT "{\n"; + print PYOUT "\treturn (PyObject *)(struct py_$struct *)type->tp_alloc(type, 0);\n"; + print PYOUT "}\n"; + print PYOUT "\n"; + print PYOUT "static void\n"; + print PYOUT "py_", $struct, "_dealloc(struct py_$struct *self)\n"; + print PYOUT "{\n"; + print PYOUT "\tPy_TYPE(self)->tp_free((PyObject *)self);\n"; + print PYOUT "}\n"; + print PYOUT "\n"; + + # Divide into single members and array members + my @singles = (); + my @arrays = (); + foreach my $m (@members) { + my ($type, $name, $array_size, $max_size) = @{$m}; + if ($array_size == -1) { + push @singles, $m; + } else { + push @arrays, $m; + } + } + + # Any non-array elements are made directly accessible to the Python interpreter + print PYOUT "static PyMemberDef py_", $struct, "_members[] = {\n"; + if (@singles) { + foreach my $m (@singles) { + my ($type, $name, $array_size, $max_size) = @{$m}; + print PYOUT "\t{ \"$name\", "; + if ($type eq "char") { + print PYOUT "T_CHAR"; + } elsif ($type eq "int8_t") { + print PYOUT "T_BYTE"; + } elsif ($type eq "int16_t") { + print PYOUT "T_SHORT"; + } elsif ($type eq "int32_t") { + print PYOUT "T_INT"; + } elsif ($type eq "int64_t") { + print PYOUT "T_LONGLONG"; + } elsif ($type eq "uint8_t") { + print PYOUT "T_UBYTE"; + } elsif ($type eq "uint16_t") { + print PYOUT "T_USHORT"; + } elsif ($type eq "uint32_t") { + print PYOUT "T_UINT"; + } elsif ($type eq "uint64_t") { + print PYOUT "T_ULONGLONG"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + die "Don't py-wrap structs yet"; + } else { + die "Unsupported type \"$type\""; + } + print PYOUT ", offsetof(struct py_$struct, x.$name), 0, \"\"},\n"; + } + } + print PYOUT "\t{}\n"; + print PYOUT "};\n"; + print PYOUT "\n"; + + # Array elements have to be accessed through ->tp_[sg]etattro() as + # tuples (int[]/uint[]) or strings (char[]) + if (@arrays) { + # The attribute get function + print PYOUT "static PyObject *\n"; + print PYOUT "py_", $struct, "_getattro(PyObject *_self, PyObject *name)\n"; + print PYOUT "{\n"; + print PYOUT "\tstruct py_$struct *self = (struct py_$struct *)_self;\n"; + print PYOUT "\n"; + print PYOUT "\tif (PyUnicode_Check(name)) {\n"; + + foreach my $m (@arrays) { + my ($type, $name, $array_size, $max_size) = @{$m}; + + print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"$name\") == 0)\n"; + if ($type eq "char") { + print PYOUT "\t\t\treturn py_rxgen_get_string(&self->x.$name, $array_size);\n"; + } elsif ($type eq "uint8_t") { + print PYOUT "\t\t\treturn py_rxgen_get_uint8(&self->x.$name, $array_size);\n"; + } elsif ($type eq "uint16_t") { + print PYOUT "\t\t\treturn py_rxgen_get_uint16(&self->x.$name, $array_size);\n"; + } elsif ($type eq "uint32_t") { + print PYOUT "\t\t\treturn py_rxgen_get_uint32(&self->x.$name, $array_size);\n"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + die "Don't py-wrap struct arrays yet"; + } else { + die "Unsupported array type \"$type\""; + } + } + + print PYOUT "\t}\n"; + print PYOUT "\n"; + print PYOUT "\treturn PyObject_GenericGetAttr(_self, name);\n"; + print PYOUT "}\n"; + print PYOUT "\n"; + + # The attribute set function + print PYOUT "static int\n"; + print PYOUT "py_", $struct, "_setattro(PyObject *_self, PyObject *name, PyObject *val)\n"; + print PYOUT "{\n"; + print PYOUT "\tstruct py_$struct *self = (struct py_$struct *)_self;\n"; + print PYOUT "\n"; + print PYOUT "\tif (PyUnicode_Check(name)) {\n"; + + foreach my $m (@arrays) { + my ($type, $name, $array_size, $max_size) = @{$m}; + + print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"$name\") == 0)\n"; + if ($type eq "char") { + print PYOUT "\t\t\treturn py_rxgen_set_string(&self->x.$name, $array_size, val);\n"; + } elsif ($type eq "uint8_t") { + print PYOUT "\t\t\treturn py_rxgen_set_uint8(&self->x.$name, $array_size, val);\n"; + } elsif ($type eq "uint16_t") { + print PYOUT "\t\t\treturn py_rxgen_set_uint16(&self->x.$name, $array_size, val);\n"; + } elsif ($type eq "uint32_t") { + print PYOUT "\t\t\treturn py_rxgen_set_uint32(&self->x.$name, $array_size, val);\n"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + die "Don't py-wrap struct arrays yet"; + } else { + die "Unsupported array type \"$type\""; + } + } + + print PYOUT "\t}\n"; + print PYOUT "\n"; + print PYOUT "\treturn PyObject_GenericSetAttr(_self, name, val);\n"; + print PYOUT "}\n"; + print PYOUT "\n"; + } + + # Emit the Python type definition + print PYOUT "static PyTypeObject py_", $struct, "Type = {\n"; + + print PYOUT "\tPyVarObject_HEAD_INIT(NULL, 0)\n"; + print PYOUT "\t\"kafs.$struct\",\t\t/*tp_name*/\n"; + print PYOUT "\tsizeof(struct py_$struct),\t/*tp_basicsize*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_itemsize*/\n"; + print PYOUT "\t(destructor)py_", $struct, "_dealloc, /*tp_dealloc*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_print*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_getattr*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_setattr*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_compare*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_repr*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_as_number*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_as_sequence*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_as_mapping*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_hash */\n"; + print PYOUT "\t0,\t\t\t\t/*tp_call*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_str*/\n"; + if (@arrays) { + print PYOUT "\tpy_", $struct, "_getattro,\n"; + print PYOUT "\tpy_", $struct, "_setattro,\n"; + } else { + print PYOUT "\t0,\t\t\t\t/*tp_getattro*/\n"; + print PYOUT "\t0,\t\t\t\t/*tp_setattro*/\n"; + } + print PYOUT "\t0,\t\t\t\t/*tp_as_buffer*/\n"; + print PYOUT "\tPy_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/\n"; + if (@comments) { + print PYOUT "\t"; + foreach my $c (@comments) { + $c =~ s/\s+$//; + $c =~ s/^\s+//; + next if ($c eq "/*" || $c eq "*/"); + $c =~ s/^[*] //; + $c =~ s/^[*]$//; + print PYOUT "\n\t\t\"", $c, "\\n\""; + } + print PYOUT ",\n"; + } else { + print PYOUT "\t\"\",\t\t\t/* tp_doc */\n"; + } + print PYOUT "\t0,\t\t\t\t/* tp_traverse */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_clear */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_richcompare */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_weaklistoffset */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_iter */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_iternext */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_methods */\n"; + if (@singles) { + print PYOUT "\tpy_", $struct, "_members,\n"; + } else { + print PYOUT "\t0,\t\t\t/* tp_members */\n"; + } + print PYOUT "\t0,\t\t\t\t/* tp_getset */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_base */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_dict */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_descr_get */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_descr_set */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_dictoffset */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_init */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_alloc */\n"; + print PYOUT "\tpy_", $struct, "_new,\t/* tp_new */\n"; + print PYOUT "};\n"; + + # Emit a function to allocate such a type + print PYHDR "extern PyObject *kafs_new_py_$struct(PyObject *, PyObject *);\n"; + + print PYOUT "\n"; + print PYOUT "PyObject *\n"; + print PYOUT "kafs_new_py_$struct(PyObject *_self, PyObject *args)\n"; + print PYOUT "{\n"; + print PYOUT "\tPyObject *obj;\n"; + print PYOUT "\tobj = _PyObject_New(&py_", $struct, "Type);\n"; + print PYOUT "\treturn obj ?: PyExc_MemoryError;\n"; + print PYOUT "}\n"; +} + +############################################################################### +# +# Emit python structure wrapper static method table +# +############################################################################### + +print PYOUT "\n"; +print PYOUT "/*\n"; +print PYOUT " * The static methods.\n"; +print PYOUT " */\n"; +print PYOUT "static PyMethodDef module_methods[] = {\n"; + +foreach my $s (@structs) { + my $struct = @{$s}[0]; + print PYOUT "\t{\"new_$struct\", (PyCFunction)kafs_new_py_$struct, METH_NOARGS,\n"; + print PYOUT "\t \"Create a new $struct record.\"\n"; + print PYOUT "\t},\n"; +} + +foreach $func (sort keys %funcs) { + my @params = @{$funcs{$func}}; + print PYOUT "\t{\"$func\", (PyCFunction)kafs_$func, METH_VARARGS, \"\" },\n"; +} + +print PYOUT "\t{\"rx_new_connection\", (PyCFunction)kafs_py_rx_new_connection, METH_VARARGS,\n"; +print PYOUT "\t\"\" },\n"; + +print PYOUT "\t{}\n"; +print PYOUT "};\n"; + +############################################################################### +# +# Emit python structure wrapper loader +# +############################################################################### +print PYOUT "\n"; + +print PYOUT "static PyModuleDef kafs_module = {\n"; +print PYOUT "\t.m_base = PyModuleDef_HEAD_INIT,\n"; +print PYOUT "\t.m_name = \"kafs\",\n"; +print PYOUT "\t.m_doc = \"AFS stuff.\",\n"; +print PYOUT "\t.m_size = -1,\n"; +print PYOUT "\t.m_methods = module_methods,\n"; +print PYOUT "};\n"; + +print PYHDR "\n"; +print PYHDR "extern PyObject *pykafs_load_wrappers(void);\n"; + +print PYOUT "\n"; +print PYOUT "PyObject *pykafs_load_wrappers(void)\n"; +print PYOUT "{\n"; +print PYOUT "\tPyObject *m;\n"; + +# Load types +if (@structs) { + print PYOUT "\tif ("; + print PYOUT "PyType_Ready(&py_rx_connectionType) < 0"; + my $first = 0; + foreach my $s (@structs) { + my @members = @{$s}; + my $struct = $members[0]; + print PYOUT " ||\n\t " unless ($first); + print PYOUT "PyType_Ready(&py_", $struct, "Type) < 0"; + $first = 0; + } + print PYOUT ")\n"; + print PYOUT "\t\treturn NULL;\n"; +} + +print PYOUT "\n"; +print PYOUT "\tm = PyModule_Create(&kafs_module);\n"; +print PYOUT "\tif (!m)\n"; +print PYOUT "\t\treturn NULL;\n"; + +if (%constants) { + print PYOUT "\n"; + foreach my $c (sort keys %constants) { + print PYOUT "\tPyModule_AddIntConstant(m, \"$c\", $c);\n"; + } +} + +if (@structs) { + print PYOUT "\n"; + foreach my $s (@structs) { + my @members = @{$s}; + my $struct = $members[0]; + print PYOUT "\tPy_INCREF(&py_", $struct, "Type);\n"; + print PYOUT "\tPyModule_AddObject(m, \"$struct\", (PyObject *)&py_", $struct, "Type);\n"; + } + + print PYOUT "\n"; + print PYOUT "\treturn m;\n"; +} + +print PYOUT "}\n"; + +############################################################################### +# +# Emit a python wrapper function to make a simple synchronous call +# +############################################################################### +sub emit_py_func_simple_sync_call($$$@) +{ + my ($func, $_request, $_reply, @params) = @_; + my @request = @{$_request}; + my @reply = @{$_reply}; + + print PYOUT "PyObject *\n"; + print PYOUT "kafs_", $func, "(PyObject *_self, PyObject *args)\n"; + print PYOUT "{\n"; + + # Declare parameter variables + my $need_tmp = 0; + print PYOUT "\tstruct py_rx_connection *z_conn;\n"; + foreach my $p (@params) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($type eq "char*") { + die "String output args not supported" unless ($dir eq "IN"); + print PYOUT "\tconst char *param_$name;\n"; + } elsif ($type eq "int8_t" || $type eq "int8_t*" || + $type eq "int16_t" || $type eq "int16_t*" || + $type eq "int32_t" || $type eq "int32_t*" || + $type eq "int64_t" || $type eq "int64_t*" || + $type eq "uint8_t" || $type eq "uint8_t*" || + $type eq "uint16_t" || $type eq "uint16_t*" || + $type eq "uint32_t" || $type eq "uint32_t*" || + $type eq "uint64_t" || $type eq "uint64_t*" + ) { + $type =~ s/[*]$//; + $need_tmp = 1 unless ($dir eq "IN"); + print PYOUT "\t$type param_$name;\n"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + die "INOUT struct args not supported" if ($dir eq "INOUT"); + print PYOUT "\tstruct py_$1 *param_$name;\n"; + } else { + die "Unsupported type \"$type\""; + } + } + + # Replies are passed back into lists provided by the caller. INOUT variable + # input values must already occupy the lists. + foreach my $p (@reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print PYOUT "\tPyObject *reply_$name;\n"; + } + + print PYOUT "\tPyObject *tmp;\n" if ($need_tmp); + print PYOUT "\tPyObject *res = NULL;\n"; + print PYOUT "\tint ret;\n"; + + # Make use of the tuple parser to extract the arguments and check their + # types for us. + print PYOUT "\n"; + print PYOUT "\tif (!PyArg_ParseTuple(args, \"O!"; + + foreach my $p (@params) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($dir ne "IN") { + print PYOUT "O!"; + } elsif ($type eq "char*") { + print PYOUT "s"; + } elsif ($type eq "int8_t") { + print PYOUT "B"; + } elsif ($type eq "int16_t") { + print PYOUT "h"; + } elsif ($type eq "int32_t") { + print PYOUT "i"; + } elsif ($type eq "int64_t") { + print PYOUT "L"; + } elsif ($type eq "uint8_t") { + print PYOUT "b"; + } elsif ($type eq "uint16_t") { + print PYOUT "H"; + } elsif ($type eq "uint32_t") { + print PYOUT "I"; + } elsif ($type eq "uint64_t") { + print PYOUT "K"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print PYOUT "O!"; + } + } + + print PYOUT "\",\n"; + print PYOUT "\t\t\t &py_rx_connectionType, &z_conn"; + + foreach my $p (@params) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + print PYOUT ",\n"; + print PYOUT "\t\t\t /*$dir*/ "; + if ($dir ne "IN") { + print PYOUT "&PyList_Type, &reply_$name"; + } elsif ($type eq "char*" || + $type eq "int8_t" || $type eq "int8_t" || + $type eq "int16_t" || $type eq "int16_t" || + $type eq "int32_t" || $type eq "int32_t" || + $type eq "int64_t" || $type eq "int64_t" || + $type eq "uint8_t" || $type eq "uint8_t" || + $type eq "uint16_t" || $type eq "uint16_t" || + $type eq "uint32_t" || $type eq "uint32_t" || + $type eq "uint64_t" || $type eq "uint64_t") { + print PYOUT "¶m_$name"; + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print PYOUT "&py_", $1, "Type, ¶m_$name"; + } else { + die "Unsupported type \"$type\""; + } + } + print PYOUT "))\n"; + print PYOUT "\t\treturn NULL;\n"; + + # Allocate reply buffer objects + if (@reply) { + print PYOUT "\n"; + foreach my $p (@reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print PYOUT "\tparam_$name = (struct py_$1 *)kafs_new_py_$1(NULL, NULL);\n"; + print PYOUT "\tif (!param_$name)\n"; + print PYOUT "\t\tgoto error_alloc_$name;\n"; + } + } + } + + # Make the call + print PYOUT "\n"; + print PYOUT "\tret = $func(\n"; + print PYOUT "\t\tz_conn->x"; + + foreach my $p (@params) { + print PYOUT ",\n"; + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($type eq "char*" || + $type eq "int8_t" || $type eq "int8_t*" || + $type eq "int16_t" || $type eq "int16_t*" || + $type eq "int32_t" || $type eq "int32_t*" || + $type eq "int64_t" || $type eq "int64_t*" || + $type eq "uint8_t" || $type eq "uint8_t*" || + $type eq "uint16_t" || $type eq "uint16_t*" || + $type eq "uint32_t" || $type eq "uint32_t*" || + $type eq "uint64_t" || $type eq "uint64_t*") { + if ($dir eq "IN") { + print PYOUT "\t\tparam_$name"; + } else { + print PYOUT "\t\t¶m_$name"; + } + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print PYOUT "\t\t¶m_$name->x"; + } else { + die "Unsupported type \"$type\""; + } + } + print PYOUT ");\n"; + print PYOUT "\tif (ret != 0) {\n"; + print PYOUT "\t\tif (ret == -1 && errno == ENOMEM)\n"; + print PYOUT "\t\t\tres = PyExc_MemoryError;\n"; + print PYOUT "\t\telse if (ret == -1)\n"; + print PYOUT "\t\t\tres = PyErr_SetFromErrno(PyExc_IOError);\n"; + print PYOUT "\t\telse\n"; + print PYOUT "\t\t\tres = PyLong_FromLong(ret);\n"; + print PYOUT "\t\tgoto error;\n"; + print PYOUT "\t}\n"; + + # Pass back any replies + if (@reply) { + print PYOUT "\n"; + foreach my $p (@reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + + my $set_null = 0; + my $var = "tmp"; + + if ($type eq "int8_t" || $type eq "int8_t*" || + $type eq "int16_t" || $type eq "int16_t*" || + $type eq "int32_t" || $type eq "int32_t*") { + print PYOUT "\ttmp = PyLong_FromLong(param_$name);\n"; + } elsif ($type eq "int64_t" || $type eq "int64_t*") { + print PYOUT "\ttmp = PyLong_FromLongLong(param_$name);\n"; + } elsif ($type eq "uint8_t" || $type eq "uint8_t*" || + $type eq "uint16_t" || $type eq "uint16_t*" || + $type eq "uint32_t" || $type eq "uint32_t*") { + + print PYOUT "\ttmp = PyLong_FromUnsignedLong(param_$name);\n"; + + } elsif ($type eq "uint64_t" || $type eq "uint64_t*") { + print PYOUT "\ttmp = PyLong_FromUnsignedLongLong(param_$name);\n"; + + } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + $var = "(PyObject *)param_$name"; + $set_null = 1; + } else { + die "Unsupported type \"$type\""; + } + + if ($var eq "tmp") { + print PYOUT "\tif (!tmp)\n"; + print PYOUT "\t\tgoto error;\n"; + } + + print PYOUT "\tif (PyList_Insert(reply_$name, 0, $var) == -1)\n"; + print PYOUT "\t\tgoto error", $need_tmp ? "_tmp" : "", ";\n"; + print PYOUT "\tparam_$name = NULL;\n" if ($set_null); + } + + } + + # Successful return + print PYOUT "\n"; + print PYOUT "\treturn PyLong_FromLong(0);\n"; + + # Error cleanups + print PYOUT "\n"; + if ($need_tmp) { + print PYOUT "error_tmp:\n"; + print PYOUT "\tPy_DECREF(tmp);\n"; + } + print PYOUT "error:\n"; + if (@reply) { + foreach my $p (reverse @reply) { + my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + if ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { + print PYOUT "\tPy_XDECREF(param_$name);\n"; + print PYOUT "error_alloc_$name:\n"; + } + } + } + + print PYOUT "\treturn res;\n"; + + # End the function + print PYOUT "}\n"; +} diff --git a/vl-test.py b/vl-test.py new file mode 100755 index 0000000..789f85e --- /dev/null +++ b/vl-test.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# +# Use VLDB record look-up-by-name to test the infrastructure. +# +# Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# + +import sys; +import getopt; +import kafs; +import dns.resolver; + +cell = "grand.central.org"; +volumes = [ "root.cell" ]; + +# The user can specify the cell and provide a list of volume names +opts, args = getopt.getopt(sys.argv[1:], "c:", [ "cell=" ]); +for o, a in opts: + if o == "-c": + cell = a; + +if args: + volumes = args; + +print("cell:", cell); + +# Find a list of Volume Location servers to contact +vladdrs = []; +try: + srv_list = dns.resolver.query("_afs3-vlserver._udp." + cell, "SRV"); + for record in srv_list: + A_recs = dns.resolver.query(record.target, 'A'); + for addr in A_recs: + vladdrs.append(addr.address); + print(record.target, ":", vladdrs); +except dns.resolver.NXDOMAIN: + print("Couldn't find any SRV records"); +except dns.resolver.NoAnswer: + print("Couldn't find any SRV records"); + +try: + afsdb_list = dns.resolver.query(cell, "AFSDB"); + for record in afsdb_list: + A_recs = dns.resolver.query(record.hostname, 'A'); + for addr in A_recs: + vladdrs.append(addr.address); + print(record.hostname, ":", vladdrs); +except dns.resolver.NoAnswer: + print("Couldn't find any AFSDB records"); + +if not vladdrs: + raise RuntimeError("Couldn't find any VL server addresses"); + +print("vladdrs:", vladdrs); + +# Go through the list of VLDB servers until one answers a probe request +for vlserver in vladdrs: + print("Trying", vlserver); + + z_conn = kafs.rx_new_connection(vlserver, kafs.AFS_VL_PORT, kafs.VL_SERVICE); + + try: + ret = kafs.VL_Probe(z_conn); + if ret == 0: + break; + except ConnectionRefusedError: + pass; + del z_conn; + +if not z_conn: + raise RuntimeError("Couldn't connect to a server"); + +# Look up each of the volumes in the list +for vol in volumes: + vldblist = []; + ret = kafs.VL_GetEntryByName(z_conn, vol, vldblist); + if ret: + raise RuntimeError("Abort occurred {:d}".format(ret)); + + for i in vldblist: + print("[", i.name, "]"); + print("\tnum\t", i.nServers); + print("\ttype\t", i.volumeType); + print("\tvid\t", i.volumeId); + print("\tflags\t {:x}".format(i.flags));