Roadshow Commands and usergroup.library

NDK 3.2 ships, in SANA+RoadshowTCP-IP/, the sources of the shell commands that configure and monitor the Roadshow TCP/IP stack, plus the SFD, headers and catalog descriptions of the companion usergroup.library. This article explains what each command does and which bsdsocket.library calls it uses, so you can treat them as worked examples, and then documents the usergroup.library API from its AmiTCP SDK 4.3 autodoc (doc/usergroup.doc) and the Roadshow SFD.

For the socket API itself see Roadshow — bsdsocket TCP/IP Stack. For SocketBaseTagList() details see AmiTCP net.lib and SocketBaseTagList Tags.

The command sources

source_code/Roadshow/README describes the directory as "the source code of the programs that call into the Roadshow bsdsocket.library to configure the TCP/IP stack and to query status information", meant "merely ... as a demonstration". The copyright lets you reuse techniques and parts of the code, but not take the code as a whole as your own. Each .c file starts with an autodoc-style block (NAME, FORMAT, TEMPLATE, OPTIONS, NOTES) and has a *_rev.h file with its version.

Command Version Template Purpose
AddNetInterface 4.57 (10.6.2019) INTERFACE/M,QUIET/S,TIMEOUT/K/N Add interfaces from configuration files and configure them (like ifconfig).
ConfigureNetInterface 4.39 (10.6.2019) INTERFACE/A,QUIET/S,ADDRESS/K,NETMASK/K,BROADCASTADDR/K,DESTINATION=DESTINATIONADDR/K,METRIC/K/N,MTU/K/N,ALIASADDR/K,DELETEADDR/K,ONLINE/S,OFFLINE/S,UP/S,DOWN/S,DEBUG/K,COMPLETE/K,CONFIGURE/K,LEASE/K,RELEASE=RELEASEADDRESS/S,ID/K,TIMEOUT/K/N,DHCPUNICAST/K Change the parameters of an interface that has already been added.
RemoveNetInterface 4.3 (11.3.2009) INTERFACE/K,QUIET/S,FORCE/S Make the stack forget an interface so it can be re-added with other parameters.
AddNetRoute 4.8 (11.3.2009) QUIET/S,DST=DESTINATION/K,HOSTDST=HOSTDESTINATION/K,NETDST=NETDESTINATION/K,VIA=GATEWAY/K,DEFAULT=DEFAULTGATEWAY/K Add routes to hosts or networks.
DeleteNetRoute 4.8 (11.3.2009) QUIET/S,DST=DESTINATION/K,DEFAULT=DEFAULTGATEWAY/K Remove a route.
GetNetStatus 4.9 (11.3.2009) CHECK/K,QUIET/S Script test: is the network operational?
ShowNetStatus 4.28 (11.3.2009) INTERFACE/M,INTERFACES/S,ARPCACHE=ARP/S,ROUTES/S,DNS=DOMAINNAMESERVERS/S,ICMP/S,IGMP/S,IP/S,MB=MEMORY/S,MR=MULTICASTROUTING/S,RT=ROUTING/S,TCP/S,UDP/S,TCPSOCKETS/S,UDPSOCKETS/S,NAMES/S,ALL/S,REPEAT/S,QUIET/S Display interfaces, routes, DNS servers and protocol statistics (like netstat).
RoadshowControl 4.9 (23.4.2017) SAVE/S,QUIET/S,GET/K,SET/K/F Show or change internal stack options.
SampleNetSpeed 4.5 (10.11.2016) INTERFACE,LEFT/N,TOP/N,WIDTH/N,HEIGHT/N,SCREEN/K Window that graphs throughput once per second.
NetShutdown 4.8 (20.7.2017) TIMEOUT/N,QUIET/S Shut the stack down in an orderly fashion.
Online 4.1 (29.4.2017) NAME/A,UNIT/N,TIMEOUT/N Put a SANA-II driver unit online.
Offline 4.1 (29.4.2017) NAME/A,UNIT/N,TIMEOUT/N Take a SANA-II driver unit offline (for hardware diagnostics).
rsh 4.10 (29.11.2019) -n=NULL/S,-l=USERNAME/K,TIMEOUT/K/N,HOST/A,COMMAND/A/F Run a command on a remote host via the shell/tcp service (port 514).

Support files: assert.c/assert.h (debug macros), compiler.h, macros.h, roadshow.h (CatComp-generated string IDs for roadshow.catalog), and swap_stack.asm/swap_stack.h.

API used by each command

Command Key calls
AddNetInterface SocketBaseTagList() probes SBTC_HAVE_INTERFACE_API, SBTC_HAVE_ROUTING_API, SBTC_HAVE_ADDRESS_CONVERSION_API; AddInterfaceTagList() with IFA_* tags; ConfigureInterfaceTagList(); BeginInterfaceConfig()/AbortInterfaceConfig() for DHCP; AddRouteTagList() (RTA_DefaultGateway, RTA_Destination); AddDomainNameServer(); SetDefaultDomainName()
ConfigureNetInterface The same interface, routing and DNS calls, without AddInterfaceTagList()
RemoveNetInterface RemoveInterface(name, force)
AddNetRoute / DeleteNetRoute AddRouteTagList() / DeleteRouteTagList() with RTA_Destination, RTA_DestinationHost, RTA_DestinationNet, RTA_Gateway, RTA_DefaultGateway
GetNetStatus One SocketBaseTags(SBTM_GETREF(SBTC_SYSTEM_STATUS), &status, TAG_END) query; tests SBSYSSTAT_* bits
ShowNetStatus ObtainInterfaceList()/QueryInterfaceTagList() with IFQ_* tags, GetRouteInfo()/FreeRouteInfo(), ObtainDomainNameServerList(), GetDefaultDomainName(), GetNetworkStatistics() with NETSTATUS_* types
RoadshowControl ObtainRoadshowData(), ChangeRoadshowData(), ReleaseRoadshowData()
SampleNetSpeed SBTC_GET_BYTES_RECEIVED/SBTC_GET_BYTES_SENT (stack totals) or QueryInterfaceTagList() with IFQ_GetBytesIn/IFQ_GetBytesOut (one interface)
NetShutdown No library calls: a message to the "TCP/IP Control" port
Online / Offline Pure SANA-II: S2_ONLINE/S2_OFFLINE plus S2_ONEVENT
rsh gethostbyname(), getservbyname("shell","tcp"), WaitSelect(), IoctlSocket(), and usergroup.library getcredentials() for the local login name

Every command that uses a Roadshow extension first checks the matching SBTC_HAVE_*_API tag and prints a "bsdsocket.library has no ... API" error if the query fails. Follow the same pattern in your own code, because AmiTCP-compatible stacks do not have these functions.

Interface configuration files

AddNetInterface reads text files from DEVS:NetInterfaces or SYS:Storage/NetInterfaces. The file name becomes the interface name (case-insensitive, at most 15 characters for historic reasons), and wildcards are allowed. Each line is one keyword, and lines starting with # or ; are comments. Unknown keywords are reported with the file name and line number.

Keyword Default / notes
DEVICE/K Required SANA-II driver; a bare name is looked up in DEVS:Networks.
UNIT/K/N 0
IPTYPE/K/N, ARPTYPE/K/N 2048 and 2054 (Ethernet packet types).
IPREQUESTS/K/N, WRITEREQUESTS/K/N, ARPREQUESTS/K/N 32, 32, 4 queued requests.
HARDWAREADDRESS/K e.g. 00:60:30:00:11:22; usually only the first configuration sticks until reboot.
POINTTOPOINT, MULTICAST, DEBUG YES/NO
DOWNGOESOFFLINE YES: marking the interface down or stopping the stack sends S2_OFFLINE.
REPORTOFFLINE YES: notify when the driver goes offline (dial-up cost control).
REQUIRESINITDELAY YES by default: about one second of delay before the first packet (original Ariadne I).
COPYMODE SLOW (16-bit transfers, Ariadne I safe) or FAST.
FILTER OFF, LOCAL (default), IPANDARP, EVERYTHING; the last two need promiscuous mode.
ADDRESS, NETMASK Dotted decimal or DHCP.
ALIAS/K/M, STATE, DESTINATION, METRIC, MTU STATE is up/down/online/offline. MTU can only lower the driver's value.
CONFIGURE DHCP (address, routes and DNS), AUTO (ZeroConf) or FASTAUTO (wireless).
LEASE, ID, DHCPUNICAST DHCP lease request (300, 30min, 2hours, infinite…), client name (2–255 chars), unicast replies.

Configuration runs in two passes: all interfaces are added first, then configured. An error in pass 1 stops everything. DHCP works only on Ethernet. The default DHCP timeout is 10 seconds, and TIMEOUT cannot make it shorter. The command can also be the default tool of a project icon (at least 4000 bytes of stack), with QUIET, TIMEOUT and PRI/PRIORITY tool types. Priority sorts the interfaces; otherwise they are sorted by name.

Stack gotcha: on AmigaOS 2.x/3.x AddNetInterface needs at least 8000 bytes of stack. If the current stack is smaller, it allocates a new one and switches to it with exec.library/StackSwap() (wrapped in swap_stack.asm). Copy this pattern if your own network tool runs deep parsing or DHCP code.

Shutting down the stack: NetShutdown

NetShutdown shows the stack's control port protocol from <libraries/bsdsocket.h>:

#define NETWORK_CONTROLLER_PORT_NAME "TCP/IP Control"
#define NCPM_Cookie 0x20040306

struct NetControlPort { struct MsgPort ncp_Port; ULONG ncp_Magic; };

struct NetShutdownMessage
{
    struct Message nsm_Message;
    ULONG          nsm_Command;  /* NSMC_Shutdown (1) or NSMC_Cancel (2) */
    APTR           nsm_Data;     /* Shutdown: ULONG * for active client count;
                                    Cancel: the shutdown message to recall */
    ULONG          nsm_Length;
    LONG           nsm_Error;    /* NSME_Success 0, Aborted 1, InUse 2,
                                    Ignored 3, NotFound 4 */
    ULONG          nsm_Actual;
};

The header says to find the port under Forbid(), check ncp_Magic and only then PutMsg(). If the cookie is missing, the controller may not be listening:

Forbid();
control_port = (struct NetControlPort *)FindPort(NETWORK_CONTROLLER_PORT_NAME);
if (control_port != NULL && control_port->ncp_Magic == NCPM_Cookie)
{
    network_in_use = TRUE;
    PutMsg(&control_port->ncp_Port, (struct Message *)shutdown_message);
}
else
    network_in_use = FALSE;
Permit();

The command then waits for the reply, a timer.device timeout (5 seconds by default) or Ctrl-C. On timeout or break it sends an NSMC_Cancel message whose nsm_Data points at the shutdown message. The shutdown cannot be stopped once it has started, and it can take longer while clients still hold network resources.

RoadshowControl and the RoadshowData API

ObtainRoadshowData(ORD_ReadAccess or ORD_WriteAccess) returns a struct List of RoadshowDataNode (rdn_Name, rdn_Flags with RDNF_ReadOnly, rdn_Type = RDNT_Integer, rdn_Length, rdn_Data). ChangeRoadshowData(list, name, length, data) sets a value, and ReleaseRoadshowData(list) gives the list back. With SAVE, the command also stores the value permanently. The equivalent environment variables are ENV:Roadshow/<group>/<name>, for example SetEnv SAVE Roadshow/ip/forwarding 1. Deleting ENVARC:Roadshow removes all saved settings. Documented option names include bpf.bufsize, icmp.maskrepl, icmp.processecho, icmp.procesststamp, if.receive.useclusters, if.minclustersize (default 208), ip.defttl, ip.forwarding, ip.sendredirects, ip.subnetsarelocal, task.controller.priority (−128..127, default 0), tcp.do_rfc1323, tcp.do_timestamps, tcp.do_win_scale, tcp.mssdflt, tcp.recvspace, tcp.rttdflt, tcp.sendspace, tcp.use_mssdflt_for_remote, udp.cksum, udp.recvspace and udp.sendspace. The source warns that wrong values "may render the TCP/IP inoperable".

GetNetStatus for scripts

CHECK takes a comma-separated list of INTERFACES, PTPINTERFACES, BCASTINTERFACES, RESOLVER, ROUTES and DEFAULTROUTE, which map one-to-one onto SBSYSSTAT_Interfaces … SBSYSSTAT_DefaultRoute. If any condition is false, the command exits with return code 5 (IF WARN). With no CHECK it prints the version and the state of every condition.

Online/Offline

These two commands talk to the SANA-II driver directly, not through the stack. They open the driver with a plain CreateIORequest(port, sizeof(struct IOSana2Req)) and no buffer-management tags, retrying with DEVS:Networks/ if needed. Then they queue S2_ONEVENT for the target state and send the command. They also contain string tables for every IOERR_*, S2ERR_* and S2WERR_* code. See SANA-II Device Driver Reference for the event logic.

Catalogs

The locale/ drawer has catalog descriptions and English/German translations for the stack and its tools:

.cd file Version Strings Catalog
bsdsocket.cd 4.15 (21.12.2013) 237 bsdsocket.catalog
roadshow.cd 4.47 (12.12.2015) 534 roadshow.catalog (opened by the commands)
usergroup.cd 4.1 (28.11.2002) 81 usergroup.catalog (errno texts for ug_StrError())
ppp-serial.cd 2.24 (18.1.2005) 234 PPP serial driver
ppp-ethernet.cd 2.27 (18.1.2005) 252 PPPoE driver

The commands open roadshow.catalog with OpenCatalogA(NULL, "roadshow.catalog", NULL) when locale.library is available. roadshow.h is the CatComp output with MSG_* IDs grouped per command (ADDNETINTERFACE_CATALOG_STRINGS …). See CatComp and Installer and Writing Localized Applications.

usergroup.library

usergroup.library provides a BSD-style interface to user and group IDs, the account and group databases, password checking and login records. It was created by the AmiTCP/IP Group (NSDi) and contains 4.3BSD Net2 code. Roadshow ships a compatible library (sfd/usergroup_lib.sfd, netinclude/libraries/usergroup.h).

Obsolete. Roadshow's <libraries/usergroup.h> states: "The usergroup.library API must be considered obsolete and should not be used in new software. It is provided solely for backwards compatibility and legacy application software." Use it only to port or maintain AmiTCP-era code.

Library model

  • Each OpenLibrary() creates a new library base instance holding the static buffers and the signals of the opening task. Only the owner task may call most functions and only the owner may close it. Ownership can be passed with UGT_OWNER.
  • Any task may call getuid(), geteuid(), getgid() and getegid(). Any task may also call getgroups(), setreuid(), setuid(), setregid(), setgid(), setgroups(), setsid() and setlogin(), but non-owners cannot recover error codes.
  • Credentials are a real UID/GID, an effective UID and up to NGROUPS (32) group IDs. In the AmiTCP implementation all tasks share one session and setsid() does nothing. The user logs in with login -f at boot.
  • User and group data come from netinfo.device (AmiTCP:db/passwd, AmiTCP:db/group). See netinfo.device and sana2.lib.
  • The AmiTCP autodoc warns that this library is not compatible with multiuser.library: the ID conventions differ and multiuser uses the AS225r2 password encoding instead of Unix crypt().

Function table

Base _UserGroupBase, bias 30. Roadshow's SFD has the same functions in the same order as the AmiTCP SDK 4.3 fd/usergroup_lib.fd.

LVO Function Registers
−30 LONG ug_SetupContextTagList(STRPTR name, struct TagItem *tags) A0, A1
−36 LONG ug_GetErr(void) —
−42 STRPTR ug_StrError(LONG err) D1
−48 LONG getuid(void) —
−54 LONG geteuid(void) —
−60 LONG setreuid(LONG real, LONG effective) D0, D1
−66 LONG setuid(LONG uid) D0
−72 LONG getgid(void) —
−78 LONG getegid(void) —
−84 LONG setregid(LONG real, LONG effective) D0, D1
−90 LONG setgid(LONG gid) D0
−96 LONG getgroups(LONG gidsetlen, LONG *gidset) D0, A1
−102 LONG setgroups(LONG gidsetlen, LONG *gidset) D0, A1
−108 LONG initgroups(STRPTR name, LONG basegid) A1, D0
−114 struct passwd *getpwnam(STRPTR login) A1
−120 struct passwd *getpwuid(LONG uid) D0
−126 VOID setpwent(void) —
−132 struct passwd *getpwent(void) —
−138 VOID endpwent(void) —
−144 struct group *getgrnam(STRPTR name) A1
−150 struct group *getgrgid(LONG gid) D0
−156 VOID setgrent(void) —
−162 struct group *getgrent(void) —
−168 VOID endgrent(void) —
−174 UBYTE *crypt(UBYTE *key, UBYTE *set) A0, A1
−180 UBYTE *ug_GetSalt(struct passwd *user, UBYTE *buf, ULONG size) A0, A1, D0
−186 STRPTR getpass(STRPTR prompt) A1
−192 ULONG umask(UWORD mask) D0
−198 ULONG getumask(void) —
−204 LONG setsid(void) —
−210 LONG getpgrp(void) —
−216 STRPTR getlogin(void) —
−222 LONG setlogin(STRPTR name) A1
−228 VOID setutent(void) —
−234 struct utmp *getutent(void) —
−240 VOID endutent(void) —
−246 struct lastlog *getlastlog(LONG uid) D0
−252 LONG setlastlog(LONG uid, STRPTR name, STRPTR host) D0, A0, A1
−258 struct UserGroupCredentials *getcredentials(struct Task *task) A0

ug_SetupContextTags(name, ...) is the varargs stub. Treat the FD/SFD as the authority over the autodoc text. For example, the autodoc's setlastlog() synopsis shows a fourth console argument, while the FD and SFD both define the three-argument form above.

Setting up the context

#define UGT_ERRNOBPTR 0x80000001
#define UGT_ERRNOWPTR 0x80000002
#define UGT_ERRNOLPTR 0x80000004
#define UGT_ERRNOPTR(size) /* picks one of the three from sizeof(errno) */
#define UGT_OWNER     0x80000011
#define UGT_INTRMASK  0x80000010
Tag Meaning
UGT_ERRNOPTR(sizeof(errno)) Redirect the library's error code into your errno (1, 2 or 4 bytes). NULL stops redirection. The header encodes the size in the tag, where the autodoc's text speaks of a separate size tag.
UGT_INTRMASK Signal mask that interrupts blocking calls. The signals are not cleared. Do not include the library's own signal.
UGT_OWNER Hand the instance to another task (or NULL, after which any task can claim it with UGT_OWNER, FindTask(NULL)).

The call returns 0 on success, or −1 with the code available from ug_GetErr(). On error the old context is cleared. AmiTCP's net.lib sets up the context like this when linked with usr.lib:

if (UserGroupBase = OpenLibrary(USERGROUPNAME, USERGROUPVERSION)) {
    if (ug_SetupContextTags(_ProgramName,
                            UGT_INTRMASK, SIGBREAKB_CTRL_C,
                            UGT_ERRNOPTR(sizeof(errno)), &errno,
                            TAG_END) == 0)
        return 0;
}

USERGROUPNAME is "usergroup.library" in the Roadshow header and "AmiTCP:libs/usergroup.library" in the AmiTCP SDK header.

Data structures

struct UserGroupCredentials        /* <libraries/usergroup.h> */
{
    LONG  cr_ruid;
    LONG  cr_rgid;
    UWORD cr_umask;
    LONG  cr_euid;
    WORD  cr_ngroups;
    LONG  cr_groups[NGROUPS];      /* NGROUPS = 32 */
    LONG  cr_session;
    TEXT  cr_login[MAXLOGNAME];    /* MAXLOGNAME = 32 */
};

struct passwd (pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell) is in <pwd.h>. struct group (gr_name, gr_passwd, gr_gid, gr_mem) is in <grp.h>. struct utmp (ut_time, ut_sid, ut_name, ut_line, ut_host) and struct lastlog (ll_time, ll_uid, ll_name, ll_line, ll_host) are in <utmp.h>. Other constants: _PASSWORD_LEN 128 (maximum getpass() length), _PASSWORD_EFMT1 '_' (extended crypt() format).

ID conversion with MultiUser. UG2MU(id) and MU2UG(id) convert between the two conventions: the super-user is 0 in usergroup and 65535 in MultiUser, and nobody is −2 in usergroup and 0 in MultiUser. IDs that do not fit in a UWORD are truncated.

Behaviour notes

  • Static results: getpw*(), getgr*(), getutent(), getlastlog(), getcredentials(), crypt() and getpass() return pointers to internal static buffers that the next call overwrites. For re-entrant access, the autodoc points to netinfo.device.
  • getpwnam()/getgrnam() etc. open the netinfo unit as needed. Call endpwent()/endgrent() when you are done. [ENOENT] means netinfo.device could not be opened, and other netinfo I/O errors come from ug_GetErr().
  • crypt(key, setting): a 9-byte setting (_ + 4 bytes iteration count + 4 bytes salt) gives the extended DES format and a 20-byte result. A 2-byte salt gives Version 7 compatibility (25 iterations, 8 key characters, 13-byte result). ug_GetSalt() generates a setting in a buffer of at least 12 bytes, Version 7 style by default.
  • getpass() reads from CONSOLE: with echo off, or from stdin/stderr if that is unavailable. Zero the returned password as soon as possible.
  • umask() defaults to 022. Its 9 low bits are used by Unix-like filesystems such as NFS.
  • setgroups(), setlogin() and changes of other users' IDs need super-user rights ([EPERM]).
  • getlastlog()/setlastlog() store only the latest user, and the utmp database allows one concurrent login.
  • ug_StrError() also understands negative Exec I/O error codes. In Roadshow its texts come from usergroup.catalog.

rsh is the NDK example that uses the library. It opens usergroup.library, calls getcredentials(NULL) and sends cr_login as the local user name to the remote rshd. Remote and local user names are truncated to 16 characters on the wire.

See Also


Sources: NDK 3.2 R4 SANA+RoadshowTCP-IP/source_code/Roadshow/.c, _rev.h, README, swap_stack.asm, roadshow.h; SANA+RoadshowTCP-IP/sfd/usergroup_lib.sfd; SANA+RoadshowTCP-IP/netinclude/libraries/usergroup.h, netinclude/libraries/bsdsocket.h; SANA+RoadshowTCP-IP/locale/*.cd; AmiTCP SDK 4.3 doc/usergroup.doc, netinclude/fd/usergroup_lib.fd, netinclude/libraries/usergroup.h, src/netlib/init_usergroup.c Updated: 2026-11-13