Index: .fossil-settings/clean-glob ================================================================== --- .fossil-settings/clean-glob +++ .fossil-settings/clean-glob @@ -49,8 +49,9 @@ tests/tests.nro tests/tests.rpx utils/objfw-config utils/objfw-new/objfw-new utils/ofarc/ofarc +utils/ofatalkcfg/ofatalkcfg utils/ofdns/ofdns utils/ofhash/ofhash utils/ofhttp/ofhttp Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -54,8 +54,9 @@ tests/tests.nro tests/tests.rpx utils/objfw-config utils/objfw-new/objfw-new utils/ofarc/ofarc +utils/ofatalkcfg/ofatalkcfg utils/ofdns/ofdns utils/ofhash/ofhash utils/ofhttp/ofhttp Index: .gitignore ================================================================== --- .gitignore +++ .gitignore @@ -54,8 +54,9 @@ tests/tests.nro tests/tests.rpx utils/objfw-config utils/objfw-new/objfw-new utils/ofarc/ofarc +utils/ofatalkcfg/ofatalkcfg utils/ofdns/ofdns utils/ofhash/ofhash utils/ofhttp/ofhttp Index: utils/Makefile ================================================================== --- utils/Makefile +++ utils/Makefile @@ -1,11 +1,12 @@ include ../extra.mk - -SUBDIRS += ${OBJFW_NEW} \ - ${OFARC} \ - ${OFDNS} \ - ${OFHASH} \ + +SUBDIRS += ${OBJFW_NEW} \ + ${OFARC} \ + ${OFATALKCFG} \ + ${OFDNS} \ + ${OFHASH} \ ${OFHTTP} include ../buildsys.mk DISTCLEAN = objfw-config ADDED utils/ofatalkcfg/Makefile Index: utils/ofatalkcfg/Makefile ================================================================== --- utils/ofatalkcfg/Makefile +++ utils/ofatalkcfg/Makefile @@ -0,0 +1,20 @@ +include ../../extra.mk + +PROG = ofatalkcfg${PROG_SUFFIX} +SRCS = OFATalkCfg.m + +include ../../buildsys.mk + +PACKAGE_NAME = ofatalkcfg + +${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2} + +CPPFLAGS += -I../../src \ + -I../../src/runtime \ + -I../../src/exceptions \ + -I../.. +LIBS := -L../../src -lobjfw \ + -L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS} \ + ${LIBS} +LD = ${OBJC} +LDFLAGS += ${LDFLAGS_RPATH} ADDED utils/ofatalkcfg/OFATalkCfg.m Index: utils/ofatalkcfg/OFATalkCfg.m ================================================================== --- utils/ofatalkcfg/OFATalkCfg.m +++ utils/ofatalkcfg/OFATalkCfg.m @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#ifdef HAVE_NET_IF_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#import "OFApplication.h" +#import "OFArray.h" +#import "OFOptionsParser.h" +#import "OFSocket.h" +#import "OFStdIOStream.h" + +#import "OFInvalidFormatException.h" + +@interface OFATalkCfg: OFObject +@end + +OF_APPLICATION_DELEGATE(OFATalkCfg) + +static void +configureInterface(OFString *interface, uint16_t network, uint8_t node, + uint8_t phase, uint16_t rangeStart, uint16_t rangeEnd) +{ + struct ifreq request = { 0 }; + struct sockaddr_at *sat; + struct atalk_netrange *nr; + int sock; + + if (interface.UTF8StringLength > IFNAMSIZ) { + [OFStdErr writeFormat: @"%@: Interface name too long!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + + strncpy(request.ifr_name, interface.UTF8String, IFNAMSIZ); + sat = (struct sockaddr_at *)&request.ifr_addr; + sat->sat_family = AF_APPLETALK; + sat->sat_net = OFToBigEndian16(network); + sat->sat_node = node; + nr = (struct atalk_netrange *)(void *)sat->sat_zero; + nr->nr_phase = phase; + nr->nr_firstnet = OFToBigEndian16(rangeStart); + nr->nr_lastnet = OFToBigEndian16(rangeEnd); + + if ((sock = socket(AF_APPLETALK, SOCK_DGRAM, 0)) < 0) { + [OFStdErr writeFormat: @"%@: Failed to create socket: %@\n", + [OFApplication programName], + OFStrError(OFSocketErrNo())]; + [OFApplication terminateWithStatus: 1]; + } + + if (ioctl(sock, SIOCSIFADDR, &request) != 0) { + [OFStdErr writeFormat: @"%@: Failed to set address: %@\n", + [OFApplication programName], + OFStrError(OFSocketErrNo())]; + [OFApplication terminateWithStatus: 1]; + } + + close(sock); +} + +@implementation OFATalkCfg +- (void)applicationDidFinishLaunching: (OFNotification *)notification +{ + OFString *nodeString = nil, *networkString = nil, *phaseString = nil; + OFString *rangeString = nil; + const OFOptionsParserOption options[] = { + { '\0', @"network", 1, NULL, &networkString }, + { '\0', @"node", 1, NULL, &nodeString }, + { '\0', @"phase", 1, NULL, &phaseString }, + { '\0', @"range", 1, NULL, &rangeString }, + { '\0', nil, 0, NULL, NULL } + }; + OFOptionsParser *optionsParser = + [OFOptionsParser parserWithOptions: options]; + OFUnichar option; + unsigned long long node, network, phase, rangeStart, rangeEnd; + OFArray OF_GENERIC(OFString *) *rangeArray; + + while ((option = [optionsParser nextOption]) != '\0') { + switch (option) { + case ':': + if (optionsParser.lastLongOption != nil) + [OFStdErr writeFormat: + @"%@: Argument for option --%@ missing\n", + [OFApplication programName], + optionsParser.lastLongOption]; + else + [OFStdErr writeFormat: + @"%@: Argument for option -%C missing\n", + [OFApplication programName], + optionsParser.lastOption]; + + [OFApplication terminateWithStatus: 1]; + break; + case '?': + if (optionsParser.lastLongOption != nil) + [OFStdErr writeFormat: + @"%@: Unknown option: --%@\n", + [OFApplication programName], + optionsParser.lastLongOption]; + else + [OFStdErr writeFormat: + @"%@: Unknown option: -%C\n", + [OFApplication programName], + optionsParser.lastOption]; + + [OFApplication terminateWithStatus: 1]; + break; + } + } + + if (optionsParser.remainingArguments.count == 0) { + [OFStdErr writeFormat: @"%@: No interface specified!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + if (optionsParser.remainingArguments.count > 1) { + [OFStdErr writeFormat: @"%@: More than one interface " + @"specified!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + + if (networkString == nil) { + [OFStdErr writeFormat: @"%@: --netwwork not specified!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + @try { + network = [networkString unsignedLongLongValueWithBase: 0]; + } @catch (OFInvalidFormatException *e) { + [OFStdErr writeFormat: @"%@: Invalid format for --network!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + if (network > UINT16_MAX) { + [OFStdErr writeFormat: @"%@: --network out of range!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + + if (nodeString == nil) { + [OFStdErr writeFormat: @"%@: --node not specified!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + @try { + node = [nodeString unsignedLongLongValueWithBase: 0]; + } @catch (OFInvalidFormatException *e) { + [OFStdErr writeFormat: @"%@: Invalid format for --node!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + if (node > UINT8_MAX) { + [OFStdErr writeFormat: @"%@: --node out of range!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + + if (phaseString == nil) { + [OFStdErr writeFormat: @"%@: --phase not specified!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + @try { + phase = [phaseString unsignedLongLongValueWithBase: 0]; + } @catch (OFInvalidFormatException *e) { + [OFStdErr writeFormat: @"%@: Invalid format for --phase!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + if (phase > 2) { + [OFStdErr writeFormat: @"%@: --phase out of range!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + + if (rangeString == nil) { + [OFStdErr writeFormat: @"%@: --range not specified!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + rangeArray = [rangeString componentsSeparatedByString: @"-"]; + if (rangeArray.count != 2) { + [OFStdErr writeFormat: @"%@: Invalid format for --range!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + @try { + rangeStart = [[rangeArray objectAtIndex: 0] + unsignedLongLongValueWithBase: 0]; + rangeEnd = [[rangeArray objectAtIndex: 1] + unsignedLongLongValueWithBase: 0]; + } @catch (OFInvalidFormatException *e) { + [OFStdErr writeFormat: @"%@: Invalid format for --range!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + if (rangeStart > UINT16_MAX || rangeEnd > UINT16_MAX) { + [OFStdErr writeFormat: @"%@: --range out of range!\n", + [OFApplication programName]]; + [OFApplication terminateWithStatus: 1]; + } + + configureInterface(optionsParser.remainingArguments.firstObject, + (uint16_t)network, (uint8_t)node, (uint8_t)phase, + (uint16_t)rangeStart, (uint16_t)rangeEnd); + + [OFApplication terminate]; +} +@end