On Sun, Oct 27, 2024 at 3:02 PM Zhaoming Luo <zhaoming1...@qq.com> wrote: > I didn't develop it in-tree because I haven't figure out how to write a > Makefile for an in-tree server. I haven't found a reference or document > for me to learn writing it. Any recommendations? Maybe I should write > one based on the Makefile of lwip and add something in the top-level > Makefile?
There is some brief documentation in the top-level Makeconf (starting with "Directory makefiles should set"...), and yes, you probably need to add your directory to prog-subdirs in the top-level Makefile. I'm not super familiar with the build system myself, but it is somewhat approachable if you know your way around Make. > >> /* 9 RTC_RD_TIME */ > >> routine pioctl_rtc_rd_time ( > >> reqport: io_t; > >> inout time: tm_t); > > Shouldn't this be 'out' rather than 'inout'? > I think my idea was that time should be a pointer. I think there may be > two method to correct it now: 1) let time be a pointer (tm_p_t?) 2) > using 'out' rather than 'inout'. > Maybe 1) is better? Because the message length will be shorter. You mention message length, which tells me that you understand that this is about messaging; good. Let me spell it out anyway: every MIG routine defines two Mach messages: a request message (from a client to a server) and a reply message (sent by the server back to the client). The request message contains 'in' parameter values ('in' being the implicit default), the reply message contains 'out' parameter values, and 'inout' parameter values are contained in both. As for tm_p_t: if you pass an actual pointer (i.e. a memory address) through MIG, the server will receive just that, a memory address in the client's address space. Which will be completely useless to it, of course. You need to actually place the data you want to pass into the message, by value. In this case the data (rtc_time_t) flows from the server to the client, so it should be an out parameter in the routine, so it gets placed in the reply message. Whether the parameter of the generated C function will be a pointer or not is secondary, getting the actual thing passed over IPC correctly should be the first concern. Next, the type you define for a parameter in MIG does not correspond 100% exactly to the type the generated C function will have; for example MIG will add a level of indirection (a pointer-to) for an out or inout parameter; so if you have routine get_foo(port: mach_port_t; out foo: int); the C signature will be kern_return_t get_foo(mach_port_t port, int *foo); And so for routine pioctl_rtc_rd_time ( reqport: io_t; out time: rtc_time_t); you'd get kern_return_t pioctl_rtc_rd_time(io_t reqport, rtc_time_t *time); on the C side, which is what you want. The request message will contain nothing, and the reply message will contain an rtc_time_t, by value. But then: the generated C pioctl_rtc_rd_time () function is not how glibc will actually invoke your RPC when someone calls ioctl(..., RTC_RD_TIME, ...). If it was done that way, glibc would have to know, in advance, all the possible ioctls, and there'd be a giant switch statement inside ioctl () implementation that would pick the right one and send the appropriate RPC. That'd be one way to do it, for sure, but that would not be extensible with out-of-tree ioctls (which is incidentally what you're trying to do). So the way this is actually done is clever, if obscure. The message id of the RPC, along with its signature, is encoded right into the ioctl number (using the _IOC () macro or one of its wrappers). The ioctl () implementation in glibc then picks apart the ioctl number, constructs the request message according to the signature, sends it off, waits for a reply from the server, and parses the reply according to the expected reply signature (also encoded in the ioctl number). The details are complicated and I won't pretend to fully understand them myself, luckily you don't have to either; the ioctl () implementation exists and works. But the important point is that on the client side, glibc doesn't use the generated C function to invoke the routine, and instead does things based on the ioctl number, so it's crucial that the definition of RTC_RD_TIME constant matches the signature of the MIG routine. On Linux, RTC_RD_TIME is defined as _IOR('p', 0x09, struct rtc_time), and IIUC you plan to keep the same definition. _IOR (..., ..., type) means an RPC that has a single out-parameter of the given type, so that's what your MIG routine should be defined as. You'll also have to define _IOT_rtc_time in a C header (rtc.h) next to the struct rtc_time definition, using the _IOT macro. Please tell me if this all makes sense, and if I need to rephrase / expand on something. > >> /* 10 RTC_SET_TIME */ > >> routine pioctl_rtc_set_time ( > >> reqport: io_t; > >> time: tm_t); > > Do I understand it right that this would be more privileged than > > reading the time? > Indeed[1]. How to make it more privileged. I didn't find information > about it in [2][3] by searching 'privilged'. Well for one thing you misspelled it :) I see that the man page for Linux says that the caller must have the CAP_SYS_TIME capability. We don't have those kinds of capabilities on the Hurd, so there are two things we could do, either: 1. require the caller to be root, i.e. for the device node to have been opened by UID 0; 2. just require the caller to have the device node opened for writing, and then rely on the Unix file access permissions mechanism to control who can do that (e.g. if it's owned by root and the mode is 0644, only root can open it for writing, but anyone can read). The second option sounds better to me, but perhaps Linux implements a different semantic for a good reason? What do other kernels do? Sergey