### Create Low-Level TCP Client Handle Source: https://context7.com/alisw/libtirpc/llms.txt Creates a connection-oriented client handle directly over a pre-opened TCP socket file descriptor. Requires manual socket setup and connection. ```c #include #include #include #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) int main(void) { int sockfd; struct sockaddr_in saddr; struct netbuf nbuf; CLIENT *clnt; struct netconfig *nconf; /* Open a TCP socket and connect to server */ sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&saddr, 0, sizeof saddr); saddr.sin_family = AF_INET; saddr.sin_port = htons(2049); /* NFS port as example */ inet_pton(AF_INET, "192.0.2.1", &saddr.sin_addr); connect(sockfd, (struct sockaddr *)&saddr, sizeof saddr); nbuf.buf = &saddr; nbuf.len = sizeof saddr; nbuf.maxlen = sizeof saddr; /* Create a connection-oriented client (0 = choose default buffer sizes) */ clnt = clnt_vc_create(sockfd, &nbuf, MYPROG, MYVERS, 0, 0); if (clnt == NULL) { clnt_pcreateerror("clnt_vc_create"); exit(EXIT_FAILURE); } /* Alternatively, use clnt_tli_create with a netconfig entry */ nconf = getnetconfigent("tcp"); CLIENT *clnt2 = clnt_tli_create(RPC_ANYFD, nconf, &nbuf, MYPROG, MYVERS, 0, 0); if (clnt2 == NULL) { clnt_pcreateerror("clnt_tli_create"); } else { clnt_destroy(clnt2); } freenetconfigent(nconf); auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } ``` -------------------------------- ### Low-Level Server Creation with svc_vc_create Source: https://context7.com/alisw/libtirpc/llms.txt Use `svc_vc_create` for connection-oriented transports (like TCP) to create individual `SVCXPRT` transport handles over pre-bound file descriptors. This method does not automatically register with `rpcbind`; use `svc_reg` afterward. The example demonstrates creating a TCP socket, binding it, and then creating the server handle. ```c #include #include #include #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) extern void my_dispatch(struct svc_req *, SVCXPRT *); int main(void) { int sockfd; struct sockaddr_in addr; SVCXPRT *xprt; struct netconfig *nconf; /* Create and bind a TCP socket on a fixed port */ sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = INADDR_ANY; bind(sockfd, (struct sockaddr *)&addr, sizeof addr); listen(sockfd, 8); /* Create a VC (connection-oriented) server handle; 0 = default buffers */ xprt = svc_vc_create(sockfd, 0, 0); if (xprt == NULL) { fprintf(stderr, "svc_vc_create failed\n"); exit(1); } /* Register the program+version with the transport, but NOT with rpcbind (pass NULL netconfig to skip rpcbind registration) */ if (!svc_reg(xprt, MYPROG, MYVERS, my_dispatch, NULL)) { fprintf(stderr, "svc_reg failed\n"); svc_destroy(xprt); exit(1); } /* Optionally register with rpcbind too */ nconf = getnetconfigent("tcp"); if (nconf) { svc_reg(xprt, MYPROG, MYVERS, my_dispatch, nconf); freenetconfigent(nconf); } svc_run(); /* never returns */ return 0; } ``` -------------------------------- ### Low-Level Server Creation with svc_tli_create, svc_vc_create, svc_dg_create Source: https://context7.com/alisw/libtirpc/llms.txt These routines create individual `SVCXPRT` transport handles over pre-bound file descriptors without automatically registering with `rpcbind`. Use `svc_reg` afterward to register. `svc_vc_create` is for connection-oriented (TCP-style) transports; `svc_dg_create` for connectionless (UDP-style); `svc_tli_create` is transport-generic. ```APIDOC ## Low-Level Server Creation — `svc_tli_create` / `svc_vc_create` / `svc_dg_create` These routines create individual `SVCXPRT` transport handles over pre-bound file descriptors without automatically registering with `rpcbind`. Use `svc_reg` afterward to register. `svc_vc_create` is for connection-oriented (TCP-style) transports; `svc_dg_create` for connectionless (UDP-style); `svc_tli_create` is transport-generic. ```c #include #include #include #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) extern void my_dispatch(struct svc_req *, SVCXPRT *); int main(void) { int sockfd; struct sockaddr_in addr; SVCXPRT *xprt; struct netconfig *nconf; /* Create and bind a TCP socket on a fixed port */ sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = INADDR_ANY; bind(sockfd, (struct sockaddr *)&addr, sizeof addr); listen(sockfd, 8); /* Create a VC (connection-oriented) server handle; 0 = default buffers */ xprt = svc_vc_create(sockfd, 0, 0); if (xprt == NULL) { fprintf(stderr, "svc_vc_create failed\n"); exit(1); } /* Register the program+version with the transport, but NOT with rpcbind (pass NULL netconfig to skip rpcbind registration) */ if (!svc_reg(xprt, MYPROG, MYVERS, my_dispatch, NULL)) { fprintf(stderr, "svc_reg failed\n"); svc_destroy(xprt); exit(1); } /* Optionally register with rpcbind too */ nconf = getnetconfigent("tcp"); if (nconf) { svc_reg(xprt, MYPROG, MYVERS, my_dispatch, nconf); freenetconfigent(nconf); } svc_run(); /* never returns */ return 0; } ``` ``` -------------------------------- ### Client Authentication with AUTH_SYS Source: https://context7.com/alisw/libtirpc/llms.txt Attach AUTH_SYS (UNIX) credentials to a `CLIENT` handle using `authsys_create_default`. This replaces the default null authentication. Remember to free the authentication handle with `auth_destroy` before destroying the client. ```c #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define SECURE_PROC ((rpcproc_t)2) int main(void) { CLIENT *clnt; enum clnt_stat stat; u_int result = 0; struct timeval tv = { 30, 0 }; clnt = clnt_create("server.example.com", MYPROG, MYVERS, "tcp"); if (clnt == NULL) { clnt_pcreateerror("create"); exit(1); } /* Replace default null auth with AUTH_SYS credentials */ auth_destroy(clnt->cl_auth); clnt->cl_auth = authsys_create_default(); if (clnt->cl_auth == NULL) { fprintf(stderr, "authsys_create_default failed\n"); clnt_destroy(clnt); exit(1); } /* Alternatively, build AUTH_SYS manually with specific uid/gid */ /* clnt->cl_auth = authsys_create("mymachine", 501, 20, 0, NULL); */ stat = clnt_call(clnt, SECURE_PROC, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_u_int, (caddr_t)&result, tv); if (stat != RPC_SUCCESS) clnt_perror(clnt, "secure call"); else printf("Result: %u\n", result); auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } ``` -------------------------------- ### Interact with rpcbind Service using rpcb_* routines Source: https://context7.com/alisw/libtirpc/llms.txt Use rpcb_getaddr to resolve service addresses, rpcb_set/unset to manage mappings, and rpcb_getmaps to retrieve all mappings. Requires rpcbind service to be running. ```c #include #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) int main(void) { struct netconfig *nconf; struct netbuf svcaddr; struct sockaddr_in addr; rpcblist *maps, *p; nconf = getnetconfigent("tcp"); if (nconf == NULL) { fprintf(stderr, "getnetconfigent: tcp failed\n"); exit(1); } /* Look up the address of MYPROG/MYVERS on the remote host via rpcbind */ memset(&addr, 0, sizeof addr); svcaddr.buf = &addr; svcaddr.maxlen = sizeof addr; svcaddr.len = 0; if (rpcb_getaddr(MYPROG, MYVERS, nconf, &svcaddr, "server.example.com")) { printf("Service found at port: %d\n", ntohs(((struct sockaddr_in *)svcaddr.buf)->sin_port)); } else { fprintf(stderr, "rpcb_getaddr: service not found (rpc_createerr set)\n"); } /* Dump all program-to-address mappings on the local host */ maps = rpcb_getmaps(nconf, "localhost"); for (p = maps; p != NULL; p = p->rpcb_next) { printf("prog=%u vers=%u addr=%s\n", p->rpcb_map.r_prog, p->rpcb_map.r_vers, p->rpcb_map.r_addr); } freenetconfigent(nconf); return 0; } ``` -------------------------------- ### Version-Negotiating Client Creation — clnt_create_vers Source: https://context7.com/alisw/libtirpc/llms.txt `clnt_create_vers` creates a `CLIENT` handle while automatically negotiating the highest mutually supported program version in the range `[vers_low, vers_high]`. The negotiated version is written back to `*vers_outp`. Returns `NULL` if no version in the range is available. ```APIDOC ## clnt_create_vers ### Description Creates a `CLIENT` handle and negotiates the highest mutually supported program version within a specified range. ### Method C Function Call ### Parameters - **host** (string) - The hostname or IP address of the server. - **prog** (rpcprog_t) - The program number of the RPC service. - **vers_outp** (rpcvers_t *) - A pointer to store the negotiated program version. - **vers_low** (rpcvers_t) - The lowest program version to attempt. - **vers_high** (rpcvers_t) - The highest program version to attempt. - **nettype** (string) - The type of network transport to use (e.g., "tcp", "udp", "netpath"). ### Request Example ```c #include #include #include #define MYPROG ((rpcprog_t)100001) int main(void) { CLIENT *clnt; rpcvers_t negotiated_vers; clnt = clnt_create_vers("server.example.com", MYPROG, &negotiated_vers, 1, /* vers_low */ 3, /* vers_high */ "netpath"); if (clnt == NULL) { clnt_pcreateerror("clnt_create_vers"); exit(EXIT_FAILURE); } printf("Negotiated RPC version: %u\n", (unsigned)negotiated_vers); /* negotiated_vers will be 1, 2, or 3 – whichever is highest on server */ auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } ``` ### Response #### Success Response A pointer to a `CLIENT` handle if successful. The negotiated version is written to `*vers_outp`. #### Response Example ```c CLIENT *clnt; // Pointer to the client handle rpcvers_t negotiated_vers; // Stores the negotiated version ``` ### Error Handling Returns `NULL` on failure. `clnt_pcreateerror` can be used to print the reason for failure. ``` -------------------------------- ### Create RPC Client Handle with Version Negotiation (clnt_create_vers) Source: https://context7.com/alisw/libtirpc/llms.txt Employ clnt_create_vers for creating a client handle that automatically negotiates the highest mutually supported program version within a specified range. The negotiated version is returned. Ensure the handle is freed using clnt_destroy. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) int main(void) { CLIENT *clnt; rpcvers_t negotiated_vers; clnt = clnt_create_vers("server.example.com", MYPROG, &negotiated_vers, 1, /* vers_low */ 3, /* vers_high */ "netpath"); if (clnt == NULL) { clnt_pcreateerror("clnt_create_vers"); exit(EXIT_FAILURE); } printf("Negotiated RPC version: %u\n", (unsigned)negotiated_vers); /* negotiated_vers will be 1, 2, or 3 – whichever is highest on server */ auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } ``` -------------------------------- ### Server Creation with svc_create Source: https://context7.com/alisw/libtirpc/llms.txt Registers a dispatch function for a given RPC program and version on all transports of the specified `nettype` class, registering with `rpcbind`. After creation, `svc_run` is called to enter the request processing loop. ```APIDOC ## Server Creation — `svc_create` `svc_create` registers a dispatch function for a given RPC program and version on all transports of the specified `nettype` class, registering with `rpcbind`. It returns the count of successfully created server handles. After creation, call `svc_run` to enter the request processing loop. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define ECHO_PROC ((rpcproc_t)1) /* Server dispatch routine */ static void my_dispatch(struct svc_req *req, SVCXPRT *xprt) { switch (req->rq_proc) { case NULLPROC: svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL); break; case ECHO_PROC: { char *str = NULL; if (!svc_getargs(xprt, (xdrproc_t)xdr_wrapstring, (caddr_t)&str)) { svcerr_decode(xprt); break; } printf("Server received: %s\n", str); if (!svc_sendreply(xprt, (xdrproc_t)xdr_wrapstring, (caddr_t)&str)) svcerr_systemerr(xprt); svc_freeargs(xprt, (xdrproc_t)xdr_wrapstring, (caddr_t)&str); break; } default: svcerr_noproc(xprt); break; } } int main(void) { int n; /* Unregister any stale registrations first */ svc_unreg(MYPROG, MYVERS); /* Register on all visible transports */ n = svc_create(my_dispatch, MYPROG, MYVERS, "netpath"); if (n == 0) { fprintf(stderr, "svc_create: failed to create any handles\n"); exit(EXIT_FAILURE); } printf("Listening on %d transport(s)\n", n); /* Enter the blocking request-processing loop (never returns) */ svc_run(); /* Unreachable */ svc_unreg(MYPROG, MYVERS); return 0; } /* Compile: cc -o server server.c -ltirpc */ ``` ``` -------------------------------- ### Low-Level Client Creation Source: https://context7.com/alisw/libtirpc/llms.txt These routines create client handles directly over a pre-opened file descriptor, bypassing rpcbind lookup. clnt_tli_create is transport-generic; clnt_vc_create targets connection-oriented transports; clnt_dg_create targets connectionless transports. ```APIDOC ## Low-Level Client Creation — `clnt_tli_create` / `clnt_vc_create` / `clnt_dg_create` These low-level routines create client handles directly over a pre-opened file descriptor, bypassing `rpcbind` lookup. `clnt_tli_create` is transport-generic; `clnt_vc_create` targets connection-oriented (TCP-style) transports; `clnt_dg_create` targets connectionless (UDP-style) transports. ```c #include #include #include #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) int main(void) { int sockfd; struct sockaddr_in saddr; struct netbuf nbuf; CLIENT *clnt; struct netconfig *nconf; /* Open a TCP socket and connect to server */ sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&saddr, 0, sizeof saddr); saddr.sin_family = AF_INET; saddr.sin_port = htons(2049); /* NFS port as example */ inet_pton(AF_INET, "192.0.2.1", &saddr.sin_addr); connect(sockfd, (struct sockaddr *)&saddr, sizeof saddr); nbuf.buf = &saddr; nbuf.len = sizeof saddr; nbuf.maxlen = sizeof saddr; /* Create a connection-oriented client (0 = choose default buffer sizes) */ clnt = clnt_vc_create(sockfd, &nbuf, MYPROG, MYVERS, 0, 0); if (clnt == NULL) { clnt_pcreateerror("clnt_vc_create"); exit(EXIT_FAILURE); } /* Alternatively, use clnt_tli_create with a netconfig entry */ nconf = getnetconfigent("tcp"); CLIENT *clnt2 = clnt_tli_create(RPC_ANYFD, nconf, &nbuf, MYPROG, MYVERS, 0, 0); if (clnt2 == NULL) { clnt_pcreateerror("clnt_tli_create"); } else { clnt_destroy(clnt2); } freenetconfigent(nconf); auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } ``` ``` -------------------------------- ### High-Level Server Creation with svc_create Source: https://context7.com/alisw/libtirpc/llms.txt Use `svc_create` to register a dispatch function for a given RPC program and version on all transports of a specified `nettype` class, automatically registering with `rpcbind`. Call `svc_run` to enter the request processing loop. Compile with `cc -o server server.c -ltirpc`. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define ECHO_PROC ((rpcproc_t)1) /* Server dispatch routine */ static void my_dispatch(struct svc_req *req, SVCXPRT *xprt) { switch (req->rq_proc) { case NULLPROC: svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL); break; case ECHO_PROC: { char *str = NULL; if (!svc_getargs(xprt, (xdrproc_t)xdr_wrapstring, (caddr_t)&str)) { svcerr_decode(xprt); break; } printf("Server received: %s\n", str); if (!svc_sendreply(xprt, (xdrproc_t)xdr_wrapstring, (caddr_t)&str)) svcerr_systemerr(xprt); svc_freeargs(xprt, (xdrproc_t)xdr_wrapstring, (caddr_t)&str); break; } default: svcerr_noproc(xprt); break; } } int main(void) { int n; /* Unregister any stale registrations first */ svc_unreg(MYPROG, MYVERS); /* Register on all visible transports */ n = svc_create(my_dispatch, MYPROG, MYVERS, "netpath"); if (n == 0) { fprintf(stderr, "svc_create: failed to create any handles\n"); exit(EXIT_FAILURE); } printf("Listening on %d transport(s)\n", n); /* Enter the blocking request-processing loop (never returns) */ svc_run(); /* Unreachable */ svc_unreg(MYPROG, MYVERS); return 0; } /* Compile: cc -o server server.c -ltirpc */ ``` -------------------------------- ### Access Network Configuration Database Source: https://context7.com/alisw/libtirpc/llms.txt Iterate through the /etc/netconfig database using setnetconfig, getnetconfig, and endnetconfig, or perform direct lookups with getnetconfigent. Requires access to the /etc/netconfig file. ```c #include #include int main(void) { void *handle; struct netconfig *ncp; /* Iterate all entries in /etc/netconfig */ handle = setnetconfig(); if (handle == NULL) { nc_perror("setnetconfig"); return 1; } while ((ncp = getnetconfig(handle)) != NULL) { printf("netid=%-8s semantics=%-12s family=%-6s proto=%s\n", ncp->nc_netid, ncp->nc_semantics == NC_TPI_CLTS ? "connectionless" : ncp->nc_semantics == NC_TPI_COTS ? "connection" : ncp->nc_semantics == NC_TPI_COTS_ORD? "conn-ord" : "other", ncp->nc_protofmly, ncp->nc_proto); } endnetconfig(handle); /* Direct lookup by netid */ struct netconfig *tcp6 = getnetconfigent("tcp6"); if (tcp6) { printf("\ntcp6 device: %s\n", tcp6->nc_device); freenetconfigent(tcp6); } else { nc_perror("getnetconfigent: tcp6"); } return 0; } /* Example output: netid=udp semantics=connectionless family=inet proto=udp netid=tcp semantics=connection family=inet proto=tcp netid=udp6 semantics=connectionless family=inet6 proto=udp netid=tcp6 semantics=connection family=inet6 proto=tcp netid=local semantics=conn-ord family=loopback proto=- */ ``` -------------------------------- ### Client Authentication — authsys_create_default / authnone_create Source: https://context7.com/alisw/libtirpc/llms.txt Manages client authentication handles, allowing for AUTH_SYS (UNIX) credentials or null authentication. These handles must be freed before destroying the client. ```APIDOC ## Client Authentication — `authsys_create_default` / `authnone_create` Authentication handles are attached to a `CLIENT` via `clnt->cl_auth`. `authsys_create_default` creates AUTH_SYS (UNIX) credentials from the current process's uid/gid. `authnone_create` creates a null-authentication handle (the library default). Both must be freed with `auth_destroy` before `clnt_destroy`. ```c #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define SECURE_PROC ((rpcproc_t)2) int main(void) { CLIENT *clnt; enum clnt_stat stat; u_int result = 0; struct timeval tv = { 30, 0 }; clnt = clnt_create("server.example.com", MYPROG, MYVERS, "tcp"); if (clnt == NULL) { clnt_pcreateerror("create"); exit(1); } /* Replace default null auth with AUTH_SYS credentials */ auth_destroy(clnt->cl_auth); clnt->cl_auth = authsys_create_default(); if (clnt->cl_auth == NULL) { fprintf(stderr, "authsys_create_default failed\n"); clnt_destroy(clnt); exit(1); } /* Alternatively, build AUTH_SYS manually with specific uid/gid */ /* clnt->cl_auth = authsys_create("mymachine", 501, 20, 0, NULL); */ stat = clnt_call(clnt, SECURE_PROC, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_u_int, (caddr_t)&result, tv); if (stat != RPC_SUCCESS) clnt_perror(clnt, "secure call"); else printf("Result: %u\n", result); auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } ``` ``` -------------------------------- ### Server Run Loop and Request Handling Source: https://context7.com/alisw/libtirpc/llms.txt Demonstrates the use of `svc_run` for a blocking server event loop and `svc_sendreply` for sending results. It also shows a custom event loop using `svc_getreq_poll`. ```APIDOC ## Server Run Loop and Request Handling — `svc_run` / `svc_sendreply` `svc_run` is the standard blocking server event loop that waits for incoming RPC requests (using `poll(2)`) and dispatches them to registered handlers. For custom event loops, `svc_getreq_poll` can be called manually. `svc_sendreply` sends the procedure's result back to the client. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define ADD_PROC ((rpcproc_t)1) /* Dispatch: implements an ADD procedure */ void arith_dispatch(struct svc_req *req, SVCXPRT *xprt) { struct { int32_t a, b; } args; int32_t result; switch (req->rq_proc) { case NULLPROC: svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL); return; case ADD_PROC: memset(&args, 0, sizeof args); if (!svc_getargs(xprt, (xdrproc_t)xdr_opaque, (caddr_t)&args)) { svcerr_decode(xprt); return; } result = args.a + args.b; if (!svc_sendreply(xprt, (xdrproc_t)xdr_int32_t, (caddr_t)&result)) svcerr_systemerr(xprt); svc_freeargs(xprt, (xdrproc_t)xdr_opaque, (caddr_t)&args); return; default: svcerr_noproc(xprt); return; } } /* Custom event loop using svc_pollset directly */ void custom_run_loop(void) { int nfds; for (;;) { /* svc_pollset is a global array of pollfd derived from svc_fdset */ nfds = poll(svc_pollset, FD_SETSIZE, -1); if (nfds > 0) svc_getreq_poll(svc_pollset, nfds); } } ``` ``` -------------------------------- ### Server Error Replies with svcerr_* Functions Source: https://context7.com/alisw/libtirpc/llms.txt Demonstrates robust RPC error handling within a dispatch function, covering authentication, argument decoding, weak authentication, and system errors. Ensure correct procedure and argument handling. ```c #include void robust_dispatch(struct svc_req *req, SVCXPRT *xprt) { /* Check authentication flavor */ if (req->rq_cred.oa_flavor != AUTH_SYS) { svcerr_auth(xprt, AUTH_REJECTEDCRED); return; } switch (req->rq_proc) { case NULLPROC: svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL); break; case 1: { int arg = 0; if (!svc_getargs(xprt, (xdrproc_t)xdr_int, (caddr_t)&arg)) { /* Argument decoding failed */ svcerr_decode(xprt); break; } if (arg < 0) { /* Weak auth / bad credentials */ svcerr_weakauth(xprt); break; } int result = arg * 2; if (!svc_sendreply(xprt, (xdrproc_t)xdr_int, (caddr_t)&result)) svcerr_systemerr(xprt); svc_freeargs(xprt, (xdrproc_t)xdr_int, (caddr_t)&arg); break; } default: svcerr_noproc(xprt); /* Procedure not implemented */ break; } } ``` -------------------------------- ### Client Handle Creation — clnt_create Source: https://context7.com/alisw/libtirpc/llms.txt `clnt_create` is the generic high-level routine for creating a `CLIENT` handle to a remote RPC program. It queries the remote `rpcbind` service to discover the server address and tries all transports of the given `nettype` class (e.g. "tcp", "udp", "netpath") in order, returning the first successful handle. The returned handle must eventually be freed with `clnt_destroy`. ```APIDOC ## clnt_create ### Description Creates a `CLIENT` handle to a remote RPC program by querying `rpcbind` and trying specified transport types. ### Method C Function Call ### Parameters - **host** (string) - The hostname or IP address of the server. - **prog** (rpcprog_t) - The program number of the RPC service. - **vers** (rpcvers_t) - The program version of the RPC service. - **nettype** (string) - The type of network transport to use (e.g., "tcp", "udp", "netpath"). ### Request Example ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) int main(void) { CLIENT *clnt; /* Create a client handle using any visible TCP/UDP transport */ clnt = clnt_create("server.example.com", MYPROG, MYVERS, "tcp"); if (clnt == NULL) { /* Print reason for failure, e.g. "clnt_create: RPC: Program not registered" */ clnt_pcreateerror("clnt_create"); exit(EXIT_FAILURE); } /* Optionally tune the timeout to 30 seconds */ struct timeval tv = { 30, 0 }; clnt_control(clnt, CLSET_TIMEOUT, (char *)&tv); /* ... make RPC calls ... */ /* Clean up: destroy auth first, then handle */ auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } /* Compile: cc -o client client.c -ltirpc */ ``` ### Response #### Success Response A pointer to a `CLIENT` handle if successful. #### Response Example ```c CLIENT *clnt; // Pointer to the client handle ``` ### Error Handling Returns `NULL` on failure. `clnt_pcreateerror` can be used to print the reason for failure. ``` -------------------------------- ### rpcbind Interface Source: https://context7.com/alisw/libtirpc/llms.txt Provides client-side routines to interact with the rpcbind service for resolving program addresses, setting/unsetting mappings, and retrieving all mappings on a host. ```APIDOC ## rpcbind Interface — `rpcb_getaddr` / `rpcb_set` / `rpcb_unset` / `rpcb_getmaps` The `rpcb_*` routines provide a direct client-side interface to the `rpcbind` service. `rpcb_getaddr` resolves a program/version/transport triple to a network address. `rpcb_set` and `rpcb_unset` manually add or remove rpcbind mappings. `rpcb_getmaps` retrieves all current program-to-address mappings on a host. ```c #include #include #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) int main(void) { struct netconfig *nconf; struct netbuf svcaddr; struct sockaddr_in addr; rpcblist *maps, *p; nconf = getnetconfigent("tcp"); if (nconf == NULL) { fprintf(stderr, "getnetconfigent: tcp failed\n"); exit(1); } /* Look up the address of MYPROG/MYVERS on the remote host via rpcbind */ memset(&addr, 0, sizeof addr); svcaddr.buf = &addr; svcaddr.maxlen = sizeof addr; svcaddr.len = 0; if (rpcb_getaddr(MYPROG, MYVERS, nconf, &svcaddr, "server.example.com")) { printf("Service found at port: %d\n", ntohs(((struct sockaddr_in *)svcaddr.buf)->sin_port)); } else { fprintf(stderr, "rpcb_getaddr: service not found (rpc_createerr set)\n"); } /* Dump all program-to-address mappings on the local host */ maps = rpcb_getmaps(nconf, "localhost"); for (p = maps; p != NULL; p = p->rpcb_next) { printf("prog=%u vers=%u addr=%s\n", p->rpcb_map.r_prog, p->rpcb_map.r_vers, p->rpcb_map.r_addr); } freenetconfigent(nconf); return 0; } ``` ``` -------------------------------- ### Perform One-Shot RPC Call with rpc_call Source: https://context7.com/alisw/libtirpc/llms.txt A simplified routine that creates a transient client, performs a single RPC call, and destroys the client in one step. Suitable for quick, stateless invocations. ```c #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define NULLPROC ((rpcproc_t)0) int main(void) { enum clnt_stat stat; /* Call the NULLPROC (procedure 0) which takes and returns nothing */ stat = rpc_call("server.example.com", MYPROG, MYVERS, NULLPROC, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL, "tcp"); if (stat != RPC_SUCCESS) { clnt_perrno(stat); /* prints: "RPC: Program not registered\n" etc. */ return 1; } printf("Null call succeeded\n"); return 0; } ``` -------------------------------- ### Create RPC Client Handle with clnt_create Source: https://context7.com/alisw/libtirpc/llms.txt Use clnt_create to establish a client handle for a remote RPC program. It discovers the server address via rpcbind and attempts specified transports. Remember to free the handle with clnt_destroy. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) int main(void) { CLIENT *clnt; /* Create a client handle using any visible TCP/UDP transport */ clnt = clnt_create("server.example.com", MYPROG, MYVERS, "tcp"); if (clnt == NULL) { /* Print reason for failure, e.g. "clnt_create: RPC: Program not registered" */ clnt_pcreateerror("clnt_create"); exit(EXIT_FAILURE); } /* Optionally tune the timeout to 30 seconds */ struct timeval tv = { 30, 0 }; clnt_control(clnt, CLSET_TIMEOUT, (char *)&tv); /* ... make RPC calls ... */ /* Clean up: destroy auth first, then handle */ auth_destroy(clnt->cl_auth); clnt_destroy(clnt); return 0; } /* Compile: cc -o client client.c -ltirpc */ ``` -------------------------------- ### RPC Service Registration with rpc_reg and svc_reg Source: https://context7.com/alisw/libtirpc/llms.txt Shows how to register RPC services using both high-level (`rpc_reg`) and low-level (`svc_reg`) interfaces. `rpc_reg` registers a single procedure on all transports, while `svc_reg` associates specific versions with dispatch functions on a given transport. Remember to unregister services using `svc_unreg`. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define MYVERS2 ((rpcvers_t)2) extern void dispatch_v1(struct svc_req *, SVCXPRT *); extern void dispatch_v2(struct svc_req *, SVCXPRT *); /* Simple procedure registered via rpc_reg (returns a static result) */ char *hello_proc(char **argp) { static char *result = "Hello from server!"; return result; } int main(void) { SVCXPRT *xprt; struct netconfig *nconf; /* High-level: register a single procedure on all "visible" transports */ if (rpc_reg(MYPROG, MYVERS, 1, hello_proc, (xdrproc_t)xdr_void, (xdrproc_t)xdr_wrapstring, "visible") != 0) { fprintf(stderr, "rpc_reg failed\n"); } /* Low-level: register two versions on the same transport */ nconf = getnetconfigent("tcp"); xprt = svc_tp_create(dispatch_v1, MYPROG, MYVERS, nconf); if (xprt) { /* Also register v2 on the same xprt */ svc_reg(xprt, MYPROG, MYVERS2, dispatch_v2, nconf); } freenetconfigent(nconf); /* At shutdown: remove all registrations for the program */ svc_unreg(MYPROG, MYVERS); svc_unreg(MYPROG, MYVERS2); return 0; } ``` -------------------------------- ### Service Registration Source: https://context7.com/alisw/libtirpc/llms.txt Shows how to register and unregister RPC services using `svc_reg`, `svc_unreg`, and `rpc_reg`. It covers both low-level and high-level registration approaches. ```APIDOC ## Service Registration — `svc_reg` / `svc_unreg` / `rpc_reg` `svc_reg` manually associates a program+version pair with a dispatch function on a specific transport handle and optionally registers with `rpcbind`. `svc_unreg` removes all rpcbind mappings and dispatch associations for a program+version. `rpc_reg` is a simpler interface that registers a single procedure on all transports of a given class. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define MYVERS2 ((rpcvers_t)2) extern void dispatch_v1(struct svc_req *, SVCXPRT *); extern void dispatch_v2(struct svc_req *, SVCXPRT *); /* Simple procedure registered via rpc_reg (returns a static result) */ char *hello_proc(char **argp) { static char *result = "Hello from server!"; return result; } int main(void) { SVCXPRT *xprt; struct netconfig *nconf; /* High-level: register a single procedure on all "visible" transports */ if (rpc_reg(MYPROG, MYVERS, 1, hello_proc, (xdrproc_t)xdr_void, (xdrproc_t)xdr_wrapstring, "visible") != 0) { fprintf(stderr, "rpc_reg failed\n"); } /* Low-level: register two versions on the same transport */ nconf = getnetconfigent("tcp"); xprt = svc_tp_create(dispatch_v1, MYPROG, MYVERS, nconf); if (xprt) { /* Also register v2 on the same xprt */ svc_reg(xprt, MYPROG, MYVERS2, dispatch_v2, nconf); } freenetconfigent(nconf); /* At shutdown: remove all registrations for the program */ svc_unreg(MYPROG, MYVERS); svc_unreg(MYPROG, MYVERS2); return 0; } ``` ``` -------------------------------- ### One-Shot RPC Call Source: https://context7.com/alisw/libtirpc/llms.txt `rpc_call` is a simplified routine that creates a transient client, performs a single RPC call, and destroys the client — all in one step. Useful for quick, stateless invocations where timeout and authentication do not need to be customized. ```APIDOC ## One-Shot RPC Call — `rpc_call` `rpc_call` is a simplified routine that creates a transient client, performs a single RPC call, and destroys the client — all in one step. Useful for quick, stateless invocations where timeout and authentication do not need to be customized. ```c #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define NULLPROC ((rpcproc_t)0) int main(void) { enum clnt_stat stat; /* Call the NULLPROC (procedure 0) which takes and returns nothing */ stat = rpc_call("server.example.com", MYPROG, MYVERS, NULLPROC, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL, "tcp"); if (stat != RPC_SUCCESS) { clnt_perrno(stat); /* prints: "RPC: Program not registered\n" etc. */ return 1; } printf("Null call succeeded\n"); return 0; } ``` ``` -------------------------------- ### Server Run Loop and Request Handling with svc_run Source: https://context7.com/alisw/libtirpc/llms.txt Implements a basic RPC server dispatch function and a custom event loop using `svc_pollset` and `svc_getreq_poll`. Ensure proper argument decoding and error handling. ```c #include #include #include #define MYPROG ((rpcprog_t)100001) #define MYVERS ((rpcvers_t)1) #define ADD_PROC ((rpcproc_t)1) /* Dispatch: implements an ADD procedure */ void arith_dispatch(struct svc_req *req, SVCXPRT *xprt) { struct { int32_t a, b; } args; int32_t result; switch (req->rq_proc) { case NULLPROC: svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL); return; case ADD_PROC: memset(&args, 0, sizeof args); if (!svc_getargs(xprt, (xdrproc_t)xdr_opaque, (caddr_t)&args)) { svcerr_decode(xprt); return; } result = args.a + args.b; if (!svc_sendreply(xprt, (xdrproc_t)xdr_int32_t, (caddr_t)&result)) svcerr_systemerr(xprt); svc_freeargs(xprt, (xdrproc_t)xdr_opaque, (caddr_t)&args); return; default: svcerr_noproc(xprt); return; } } /* Custom event loop using svc_pollset directly */ void custom_run_loop(void) { int nfds; for (;;) { /* svc_pollset is a global array of pollfd derived from svc_fdset */ nfds = poll(svc_pollset, FD_SETSIZE, -1); if (nfds > 0) svc_getreq_poll(svc_pollset, nfds); } } ```