netinfo.device and sana2.lib¶
The AmiTCP/IP SDK 4.3 contains two small but useful pieces that sit beside bsdsocket.library. netinfo.device is an Exec device that reads and writes the user and group databases, and usergroup.library is built on top of it. sana2.lib is a link library that turns SANA-II error codes into text. The SDK's devtools.doc also documents the build tools it ships (cpp, rpcgen, rcsrev). This article covers all three, from doc/netinfo.doc, doc/sana2lib.doc, doc/devtools.doc, the headers in netinclude/ and the sources in src/netlib/.
These are AmiTCP SDK components. See Roadshow Commands and usergroup.library for the usergroup.library API, which the NDK's Roadshow headers also cover.
netinfo.device¶
The SDK release notes for AmiTCP/IP 4.0 introduce devs/netinfo.device as a "new device, which handles the actual user/group database files". Its autodoc describes the aim as giving a "uniform and well-defined interface" to network databases, with one unit per database, so clients can be moved to other environments. The autodoc says it currently gives access to the local password and group files, and that file notification support is new since the previous release.
Opening¶
The device lives at AmiTCP:devs/netinfo.device. Use the header's macro:
#include <devices/netinfo.h>
#define NETINFONAME "AmiTCP:devs/netinfo.device"
#define NETINFO_PASSWD_UNIT 0L
#define NETINFO_GROUP_UNIT 1L
#define NETINFO_UNITS 2L
The I/O request is a struct NetInfoReq. Its layout matches an IOStdReq, but the last field is documented as the search criteria and is used that way by NI_MEMBERS:
struct NetInfoReq {
struct Message io_Message;
struct Device *io_Device;
struct Unit *io_Unit;
UWORD io_Command;
UBYTE io_Flags;
BYTE io_Error;
ULONG io_Actual; /* actual number of bytes transferred */
ULONG io_Length; /* requested number bytes transferred */
APTR io_Data; /* points to data area */
ULONG io_Offset; /* search criteria */
};
Commands¶
| Command | Value | Inputs | Results |
|---|---|---|---|
CMD_RESET 1.3+ |
1 | — | Restart sequential reading. |
CMD_READ 1.3+ |
2 | io_Data = entry buffer, io_Length = its length |
Next entry; io_Actual = entry length. |
CMD_WRITE 1.3+ |
3 | io_Data = entry structure |
Adds the entry, or replaces the entry with the same name. |
CMD_UPDATE 1.3+ |
4 | — | Writes changes to permanent storage. Until then, CMD_WRITE changes need not be visible to CMD_READ or saved. |
NI_GETBYID |
CMD_NONSTD+0 (9) |
io_Data = buffer with the ID member set, io_Length |
Entry with that UID/GID. |
NI_GETBYNAME |
CMD_NONSTD+1 (10) |
io_Data = buffer whose name member points to a C string, io_Length |
Entry with that name. |
NI_MEMBERS |
CMD_NONSTD+2 (11) |
Group unit only. io_Data = LONG array, io_Length = its size in bytes, io_Offset = pointer to a user name |
The IDs of the groups that list the user as a member; io_Actual = bytes used. |
NI_END (CMD_NONSTD+3) marks the end of the range. Every command returns 0 or an error in io_Error.
Naming gotcha: the autodoc overview calls the search commands
NI_BYNAMEandNI_BYUID, but<devices/netinfo.h>defines them asNI_GETBYNAMEandNI_GETBYID. Use the header names.
Errors¶
The non-standard error codes reuse BSD errno numbers:
| Code | Value |
|---|---|
NIERR_NOTFOUND |
2 |
NIERR_TOOSMALL |
7 |
NIERR_NOMEM |
12 |
NIERR_ACCESS |
13 |
NIERR_NULL_POINTER |
14 |
NIERR_INVAL |
22 |
Entry structures¶
struct NetInfoPasswd
{
UBYTE *pw_name; /* Username */
UBYTE *pw_passwd; /* Encrypted password */
LONG pw_uid; /* User ID */
LONG pw_gid; /* Group ID */
UBYTE *pw_gecos; /* Real name etc */
UBYTE *pw_dir; /* Home directory */
UBYTE *pw_shell; /* Shell */
};
struct NetInfoGroup
{
UBYTE *gr_name; /* Group name */
UBYTE *gr_passwd; /* Password */
LONG gr_gid; /* Group ID */
UBYTE **gr_mem; /* Member list */
};
These mirror struct passwd and struct group from <pwd.h> and <grp.h>. That is why usergroup.library can map getpwnam(), getgrgid() and the enumerators onto this device. The usergroup autodoc recommends talking to netinfo.device directly when you need re-entrant access, because the library functions return static buffers, and calls it "more convenient" for multitasking applications.
Example: look up a user by name¶
#include <exec/types.h>
#include <exec/io.h>
#include <devices/netinfo.h>
#include <proto/exec.h>
#include <proto/dos.h>
/* Room for the structure plus the strings the device fills in. */
static ULONG entry_buffer[256];
BOOL lookup_user(STRPTR login)
{
struct MsgPort *port;
struct NetInfoReq *nreq;
struct NetInfoPasswd *pw = (struct NetInfoPasswd *)entry_buffer;
BOOL found = FALSE;
if ((port = CreateMsgPort()) != NULL)
{
nreq = (struct NetInfoReq *)CreateIORequest(port, sizeof(*nreq));
if (nreq != NULL)
{
if (OpenDevice(NETINFONAME, NETINFO_PASSWD_UNIT,
(struct IORequest *)nreq, 0) == 0)
{
pw->pw_name = login;
nreq->io_Command = NI_GETBYNAME;
nreq->io_Data = entry_buffer;
nreq->io_Length = sizeof(entry_buffer);
if (DoIO((struct IORequest *)nreq) == 0)
{
Printf("%s uid=%ld gid=%ld home=%s\n",
pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir);
found = TRUE;
}
CloseDevice((struct IORequest *)nreq);
}
DeleteIORequest((struct IORequest *)nreq);
}
DeleteMsgPort(port);
}
return found;
}
To enumerate the database instead, send CMD_RESET once and then CMD_READ until it fails.
Database file formats¶
The password unit reads AmiTCP:db/passwd: one record per line, seven fields separated by a bar |:
name|password|uid|gid|gecos|home_dir|shell
gecosholds comma-separated sub-fields (full name, office, work phone, home phone), used byfinger.home_diris a full DOS path, and an emptyshellmeans the CLI.- The device does not restrict the password format, but
usergroup.libraryuses the Version 7 UNIX (POSIX) / 4.4BSDcrypt()format. The MultiUser 1.5 / AS225r2 format also works but is "strongly discouraged". An empty password field lets anyone log in, which the autodoc calls "almost invariably a mistake", and the file should not be readable by unprivileged users. - Login names must not start with
-, and should avoid upper case and dots, which confuse mailers. No field may contain|.
The group unit reads AmiTCP:db/group: four |-separated fields per line:
group|passwd|gid|member,member,...
The password field is rarely used and is normally *. Members are separated by commas without spaces. A user's primary group from db/passwd applies automatically and does not need to be listed here.
Apart from the password field, the formats are those used by MultiUser Library 1.3 and AS225r2.
sana2.lib — SANA-II error messages¶
sana2.lib (and its far-data variant sana2nb.lib) is built from src/netlib/sana2perror.c, sana2errlist.c and sana2printfault.c. Its declarations are in the AmiTCP SDK's <net/sana2errno.h>.
#include <devices/sana2.h>
#include <net/sana2errno.h>
void sana2perror(const char *banner, struct IOSana2Req *ios2); /* stderr */
void Sana2PrintFault(const char *banner, struct IOSana2Req *ios2); /* Output() */
extern const char * const io_errlist[]; extern const short io_nerr;
extern const char * const sana2io_errlist[]; extern const short sana2io_nerr;
extern const char * const sana2wire_errlist[];extern const short sana2wire_nerr;
Both functions print banner: <io error> and add (<wire error>) when ios2_WireError is non-zero and has a table entry. sana2perror() writes to stderr with fprintf(), so it pulls in C stdio. Sana2PrintFault() uses dos.library VPrintf() to Output(), so it suits programs without stdio. The lookup follows the SANA-II sign convention:
WORD err = ios2->ios2_Req.io_Error;
ULONG werr = ios2->ios2_WireError;
if (err >= sana2io_nerr || -err > io_nerr)
text = io_errlist[0]; /* "Unknown error" */
else if (err < 0)
text = io_errlist[-err]; /* IOERR_OPENFAIL ... IOERR_SELFTEST */
else
text = sana2io_errlist[err]; /* S2ERR_NO_ERROR ... S2ERR_TX_FAILURE */
| Table | Entries | Covers |
|---|---|---|
io_errlist |
8 | 0 ("Unknown error") and IOERR_OPENFAIL (−1) … IOERR_SELFTEST (−7), indexed by -err |
sana2io_errlist |
12 | S2ERR_NO_ERROR 0 … S2ERR_TX_FAILURE 11, with placeholders for 2 and 7 |
sana2wire_errlist |
19 | S2WERR_GENERIC_ERROR 0 … S2WERR_RCVREL_HDW_ERR 18, with a placeholder for 14 |
Coverage gotcha: the wire table dates from 1994 and ends at 18. The wire codes 19–23 from the SANA-II R4 proposal (
S2WERR_UNIT_DISCONNECTED…S2WERR_AUTHENTICATION_FAILED, see SANA-II Device Driver Reference) fall outsidesana2wire_nerr, so these functions print the I/O error alone. Add your own strings if your tool can meet them.
Alternatives on a running stack: SocketBaseTagList() with SBTC_S2ERRNOSTRPTR and SBTC_S2WERRNOSTRPTR returns the stack's own strings. Roadshow's autodoc marks both tags deprecated (see AmiTCP net.lib and SocketBaseTagList Tags).
Typical use in a SANA-II tool:
if (DoIO((struct IORequest *)ios2) != 0)
Sana2PrintFault("S2_CONFIGINTERFACE", ios2);
Development tools (devtools.doc)¶
The SDK's bin/ drawer holds three tools documented in doc/devtools.doc.
cpp¶
The Decus C preprocessor by Martin Minow: cpp [-options] [infile [outfile]], reading stdin/stdout if no files are given (- also means stdin/stdout). Options may be given in either case:
| Option | Effect |
|---|---|
-C |
Keep comments in the output |
-Dname[=value] |
Define (value defaults to 1) |
-E |
Always return success (fatal errors such as a missing include still fail) |
-Idirectory |
Add an include directory (no space after -I) |
-N |
Do not predefine target symbols. -N -N also drops __LINE__, __FILE__, __DATE__. |
-P |
No #line output |
-Stext, -S*text |
Override the sizes of the basic types (and of pointers) for #if sizeof |
-Uname |
Undefine |
-Xnumber |
Debug CPP itself |
It implements a 1984 draft of ANSI C (#elif, '\x' escapes, #if defined, #if sizeof(type), token concatenation with #). The #if evaluator uses signed integers only. rpcgen runs this preprocessor on its input.
rpcgen¶
The Sun RPC protocol compiler. rpcgen proto.x produces proto.h, proto_xdr.c, proto_svc.c and proto_clnt.c (plus proto_tbl.i with -T). Single outputs are selected with -c, -h, -l, -m, -t, -s nettype or -n netid. Other switches: -a (all files including samples), -Sc/-Ss (client/server sample code), -C (ANSI C, the default), -k (K&R), -N (new style with multiple arguments), -I (inetd-startable server), -K secs (server exit delay, default 120), -b/-5 (SunOS 4.1 or SVR4 style; -b is the default). The preprocessor symbols RPC_HDR, RPC_XDR, RPC_SVC, RPC_CLNT and RPC_TBL are defined for the matching output, and lines starting with % are copied through unchanged. The RPC language does not nest structures. The SDK's src/examples/rpc/ (dir, msg, sort) and help/RPC.guide show it in use with rpcr.lib/rpcdr.lib.
rcsrev¶
RCSREV NAME/A SOURCE/A POSTFIX ASM=ASMINCLUDE/S DATE/K turns a source file's RCS $Id$ string into a BumpRev-compatible NAME_rev.h (and NAME_rev.i with ASMINCLUDE). The autodoc also lists PREFIX/K. DATE selects CURRENT (default), RCS, SASC (__AMIGADATE__) or DICE (__COMMODORE_DATE__). With the SASC or DICE formats the DATE macro is not defined in the output, VSTRING is not defined with SASC, and neither format can be combined with ASMINCLUDE. Only the Id RCS keyword is recognised.
See Also¶
- Roadshow Commands and usergroup.library
- AmiTCP net.lib and SocketBaseTagList Tags
- SANA-II Device Driver Reference
- Roadshow — bsdsocket TCP/IP Stack
- Device Command Set Rules
Sources: AmiTCP SDK 4.3 doc/netinfo.doc, doc/sana2lib.doc, doc/devtools.doc, doc/usergroup.doc, netinclude/devices/netinfo.h, netinclude/net/sana2errno.h, src/netlib/sana2perror.c, src/netlib/sana2errlist.c, src/netlib/sana2printfault.c, src/netlib/Smakefile, "AmiTCP SDK - Read Me"; NDK 3.2 R4 Include_H/exec/io.h Updated: 2026-11-13