Локальные сети персональных компьютеров. Работа с сервером Novell NetWare© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 168 стр. 2.4. Подключение к файл-серверуСоздав канал с файл-сервером, программа еще не получила доступ к томам сервера и другому сервису. Следующим после создания канала этапом должно быть подключение пользователя к файл-серверу. Для подключения пользователя к файл-серверу вы должны использовать функцию LoginToFileServer() из библиотеки Novell NetWare C Interface: int LoginToFileServer(char *ObjectName, WORD ObjectType, char *ObjectPassword); Первый параметр ObjectName - указатель на имя пользователя, под которым его зарегистрировал супервизор сети или руководитель группы. Второй параметр определяет тип объекта. Для пользователя вы долны задать значение 1. Последний параметр - указатель на текстовую строку, содержащую пароль пользователя. Учтите, что и имя пользователя, и его пароль должны задаваться заглавными буквами. Функция LoginToFileServer() выполняет достаточно сложную процедуру шифровки пароля, поэтому без использования библиотеки NetWare C Interface или аналогичных средств вы только с большим трудом сможете выполнить процедуру подключения к серверу без этой функции. Кстати, в отличие от других функций, исходный текст функции LoginToFileServer() и некоторых других не входит в комплект поставки библиотеки NetWare C Interface. Есть функции и для отключения пользователя от одного или сразу ото всех файл-серверов. С помощью функции Logout() вы можете отключиться сразу ото всех файл-серверов: void Logout(void); Функция LogoutFromFileServer() предназначена для отключения только от одного сервера, номер канала которого задается в качестве единственного параметра функции: void LogoutFromFileServer(WORD ConnectionID); В разделе "Программа LOG" мы приведем программу, которая умеет подключать пользователя к файл-серверу, а сейчас займемся тем, что определим список активных файл-серверов. 2.4.1. Программа SLISTМы подготовили для вас программу, которая, пользуясь протоколом SAP, определяет список активных серверов и запоминает имена серверов. Затем для всех активных серверов программа получает дополнительную информацию и выводит ее в стандартный поток вывода. Программа создает объект класса SLIST. Конструктор этого объекта получает всю необходимую информацию, которая при помощи функции SLIST::PrintServersName(), определенной в классе SLIST, выводится в стандартный поток (листинг 3). // ================================================================ // Листинг 3. Просмотр списка активных серверов и вывод в стандарт- // ный поток имен и другой информации об активных серверах // Файл slist!\slist.cpp // // (C) A. Frolov, 1993 // ================================================================ #include <stdlib.h> #include <stdio.h> #include <mem.h> #include <string.h> #include <dos.h> #include <direct.h> #include "sap.hpp" void main(void) { SLIST *ServerList; int ccode = 0; printf("\n*SLIST!*, v.1.0, (C) Фролов А.В., 1993\n"); // Создаем объект класса SLIST. Конструктор этого объекта // получает всю необходимую информацию о серверах и // записывает ее в область данных объекта ServerList = new SLIST(GENERAL_SERVICE); // Если при создании объекта были ошибки, завершаем // выполнение программы ccode = ServerList->Error(); if(ccode) { printf("Ошибка %d\n", ccode); return; } // Выводим список серверов printf("\nОбнаружены серверы:\n"); printf( "---------------------------------------------" "------------------------------\n"); ServerList->PrintServersName(); printf( "---------------------------------------------" "------------------------------\n"); } Файл slist.cpp содержит определения функций-членов класса SLIST (листинг 4). Конструктор SLIST() проверяет наличие сетевой оболочки, проверяет и запоминает тип запроса (получить сведения о ближайшем сервере или о всех серверах сети) и запоминает его. Затем конструктор инициализирует драйвер протокола IPX и открывает динамический короткоживущий сокет для работы с протоколом SAP. Далее в цикле создаются блоки ECB и ставятся в очередь на прием пакетов. Эти блоки ECB будут использованы для приема SAP-пакетов. После подготовки ECB конструктор посылает пакет запроса, ожидает примерно одну секунду и при помощи функций SLIST::GetServersName() и SLIST::GetServersInfo() получает и запоминает имена серверов и другую информацию. Для работы с IPX-пакетами мы использовали функции из библиотеки NetWare C Interface. Назначение этих функций вам будет понятно из их названия, если вы прочитали предыдущий том "Библиотеки системного программиста". Функция IPXInitialize() проверяет наличие драйвера протокола IPX и выполняет все инициализирующие действия, необходимые для использования протокола IPX. Функция IPXOpenSocket() предназначена для открытия сокета. Первый параметр функции - указатель на переменную типа WORD, содержащую значение открываемого сокета или ноль, если надо получить динамический сокет. Байты в этой переменной расположены в обратном порядке, т. е. старший байт расположен по младшему адресу. Второй параметр функции IPXOpenSocket() определяет тип открываемого сокета - долгоживущий или короткоживущий. В нашем случае мы открываем динамический короткоживущий сокет. После открытия сокета конструктор с помощью функции SLIST::ReceiveSAPPacket() подготавливает массив блоков ECB для приема ответных пакетов и, вызывая функцию IPXListenForPacket(), ставит эти блоки в очередь на прием. Функция IPXListenForPacket() имеет в качестве единственного параметра указатель на блок ECB. Далее конструктор вызывает функцию SLIST::SendSAPPacket(), которая подготавливает блок ECB и заголовок IPX-пакета для SAP-запроса. При этом с помощью функции IPXGetInternetworkAddress() программа определяет свой собственный сетевой адрес. Функция IPXGetInternetworkAddress() имеет в качестве параметра указатель на структуру, в которую будет записан номер сети и сетевой адрес узла в сети. Подготовив пакет, функция SLIST::SendSAPPacket() ставит его в очередь на передачу при помощи функции IPXSendPacket(), передавая ей в качестве параметра указатель на соответствующий блок ECB. Когда пакет будет передан, конструктор ждет примерно одну секунду. В течение этого времени приходят ответные пакеты от серверов. После ожидания вызываются функции SLIST::GetServersName() и SLIST::GetServersInfo(), получающие соответсвенно имена серверов и дополнительную информацию. Функция SLIST::GetServersName() переписывает имена откликнувшихся на запрос серверов из принятых SAP-пакетов во внутренний массив объекта класса SLIST. Функция SLIST::GetServersInfo() выполняет более сложные действия. Вначале с помощью функций GetPrimaryConnectionID() и GetDefaultConnectionID() она получает номера каналов первичного и текущего серверов, записывая их во внутренние переменные объекта класса SLIST. Затем запускается цикл по всем обнаруженным в сети серверам. Внутри этого цикла для каждого сервера функция получает его номер канала при помощи функции GetConnectionID(). Если канала нет, рабочая станция создает его, подключаясь к серверу. Для подключения используется функция AttachToFileServer(). Затем сервер делается предпочтительным, для чего вызывается функция SetPreferredConnectionID(). Теперь все запросы будут идти к предпочтительному серверу. Внутри цикла мы по очереди будем делать все имеющиеся серверы предпочтительными и, направляя запросы, получать от серверов интересующую нас информацию. Далее функция SLIST::GetServersInfo() вызывает функцию GetServerInformation(), которая записывает сведения о сервере в структуру ServerInfo. Первый параметр функции GetServerInformation() задает размер этой структуры, а второй является указателем на нее. Перед возвратом функция SLIST::GetServersInfo() пытается получить серийный номер операционной системы Novell NetWare, работающей на предпочтительном файл-сервере, вызывая функцию GetNetworkSerialNumber(). Этой функции в качестве первого параметра необходимо передать указатель на переменную типа long, в качестве второго - указатель на переменную типа WORD. В первую переменную функция запишет серийный номер операционной системы, во вторую - серийный номер приложения, работающего на файл-сервере. Надо заметить, что данная функция возвращает серийный номер только для тех серверов, к которым было выполнено подключение пользователя функцией LoginToFileServer(). Поэтому перед вызовом функции GetNetworkSerialNumber() мы записываем в поле серийного номера и номера приложения нулевое значение. Если содержимое этих полей останется нулевым, значит, пользователь не подключился к данному файл-серверу. Для сокращения размера листинга мы не проверяем код ошибки, возвращаемый функцией GetNetworkSerialNumber(). Функция SLIST::PrintServersName() в цикле для всех обнаруженных серверов выводит в стандартный поток вывода имя сервера, напротив которого указывается, является ли он первичным (Primary) или текущим (Default). Затем выводится версия Novell NetWare, взятая из полей netwareVersion и netwareSubVersion структуры ServerInfo. Для подключенных серверов выводится серийный номер и номер приложения. Далее для всех серверов выводится номер канала, используемого сервером и записанного ранее в массив ConnID[]. После этого для каждого сервера выводится содержимое полей maxConnectionsSupported и connectionsInUse структуры ServerInfo, которые содержат максимальное количество каналов для сервера и количество каналов, используемых в данный момент. Перед окончанием работы программы вызывается деструктор, который отменяет все ожидающие приема блоки ECB и закрывает динамический сокет. Для отмены блоков ECB используется функция IPXCancelEvent(), которой в качестве параметра передается указатель на отменяемый блок ECB. Сокет закрывается при помощи функции IPXCloseSocket(). Номер закрываемого сокета передается этой функции в качестве параметра. // =================================================== // Листинг 4. Функции для программы SLIST.CPP // Файл slist!\sap.cpp // // (C) A. Frolov, 1993 // =================================================== #include <stdlib.h> #include <stdio.h> #include <mem.h> #include <string.h> #include <dos.h> #include "sap.hpp" // ==================================================== // Конструктор класса SLIST // ==================================================== SLIST::SLIST(int ServiceType) { // Проверяем наличие сетевой оболочки и определяем ее версию MajorVersion = 0; asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si // Если оболочка не загружена, завершаем работу // программы с сообщением об ошибке if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); errno = 0xff; return; } // Проверяем тип SAP-запроса if (ServiceType != 1 && ServiceType != 3) { errno = NOT_SUPPORTED; return; } // Запоминаем тип запроса QueryType = ServiceType; // Инициализируем драйвер протокола IPX IPXInitialize(); // Открываем короткоживущий динамический сокет SrcSocket = 0x00; errno = IPXOpenSocket(&SrcSocket, SHORT_LIVED); // Заполняем таблицу имен серверов нулями memset(ServerName,0,sizeof(ServerName)); // Подготавливаем блоки ECB для приема // пакетов от SAP-протокола for(int i=0;i<MAX_SERVERS;i++) { // Заполняем блок ECB ReceiveSAPPacket(&Query[i]); // Ставим в очередь на прием пакета IPXListenForPacket(&Query[i].theECB); } // Если не было ошибок, посылаем запрос if (!errno) { SendSAPPacket(); // Ждем примерно одну секунду sleep(1); // Переписываем имена серверов и другую информацию GetServersName(); GetServersInfo(); } } // ==================================================== // Деструктор класса SLIST // ==================================================== SLIST::~SLIST() { // Отменяем ожидающие блоки ECB for(int i=0;i<MAX_SERVERS;i++) { IPXCancelEvent(&Query[i].theECB); } // Закрываем сокет IPXCloseSocket(SrcSocket); } // ==================================================== // Посылка SAP-запроса // ==================================================== void SLIST::SendSAPPacket(void) { // Сбрасываем поле inUseFlag и ESRAddress, устанавливаем тип пакета 0 SendPacket.theECB.inUseFlag = 0; SendPacket.theECB.ESRAddress = 0; SendPacket.SAPq.packetType = 0; // SAP-пакет состоит из одного фрагмента. Записываем в ECB // количество фрагментов, адрес и размер буфера SendPacket.theECB.fragmentCount = 1; SendPacket.theECB.fragmentDescriptor[0].address = &SendPacket.SAPq; SendPacket.theECB.fragmentDescriptor[0].size = sizeof(SAPQueryPacket); // Записываем в ECB номер своего сокета SendPacket.theECB.socketNumber = SrcSocket; // Устанавливаем адрес назначения - все станции в текущей сети, // сокет SAP_SOCKET. Устанавливаем поле непосредственного адреса memset(SendPacket.SAPq.destination.network, '\x00', 4); memset(SendPacket.SAPq.destination.node, '\xFF', 6); SendPacket.SAPq.destination.socket = IntSwap(SAP_SOCKET); memset(SendPacket.theECB.immediateAddress, '\xFF', 6); // Устанавливаем свой адрес в заголовке запроса IPXGetInternetworkAddress(SendPacket.SAPq.source.network); SendPacket.SAPq.source.socket = IntSwap(SrcSocket); // Заполняем передаваемый пакет. Устанавливаем тип запроса // и тип сервера SendPacket.SAPq.queryType = IntSwap(QueryType); SendPacket.SAPq.serverType = IntSwap(0x0004); // Посылаем SAP-пакет IPXSendPacket(&SendPacket.theECB); // Ожидаем завершения процесса передачи пакета while (SendPacket.theECB.inUseFlag) IPXRelinquishControl(); // Сохраняем код возврата errno = SendPacket.theECB.completionCode; } // ==================================================== // Прием SAP-пакетов // ==================================================== void SLIST::ReceiveSAPPacket(RECEIVE_PACKET *Query) { // Сбрасываем поле inUseFlag и ESRAddress Query->theECB.inUseFlag = 0; Query->theECB.ESRAddress = 0; // Записываем в ECB количество фрагментов, адрес и размер буфера Query->theECB.fragmentCount = 1; Query->theECB.fragmentDescriptor[0].address = &Query->SB; Query->theECB.fragmentDescriptor[0].size = sizeof(Query->SB); // Устанавливаем в ECB свой номер сокета Query->theECB.socketNumber = SrcSocket; } // ==================================================== // Процедура переписывает имена серверов из тех // блоков ECB, для которых пришли пакеты // ==================================================== void SLIST::GetServersName(void) { for(int i=0,j=0; i<MAX_SERVERS; i++) { if(!Query[i].theECB.inUseFlag) { strcpy(ServerName[j],Query[i].SB.ServerName); j++; } } } // ==================================================== // Процедура получает информацию о серверах // ==================================================== void SLIST::GetServersInfo(void) { // Получаем номера каналов первичного сервера // и сервера по умолчанию PrimaryConnID = GetPrimaryConnectionID(); DefaultConnID = GetDefaultConnectionID(); // Цикл по всем обнаруженным в сети активным серверам for(int i=0; i<MAX_SERVERS; i++) { if(ServerName[i][0]) { // Получаем номер канала сервера errno = GetConnectionID(ServerName[i], &ConnID[i]); // Если канала нет, создаем его, подключаясь к серверу if(errno) { AttachToFileServer(ServerName[i], &ConnID[i]); } // Делаем текущий сервер предпочтительным, так как // именно к нему должны поступать запросы errno = SetPreferredConnectionID(ConnID[i]); // Получаем информацию о текущем сервере if(!errno) errno = GetServerInformation(sizeof(ServerInfo[i]), &ServerInfo[i]); // Получаем серийный номер и номер приложения SerialNumber[i]=ApplicationNumber[i]=0L; errno = GetNetworkSerialNumber(&SerialNumber[i], &ApplicationNumber[i]); errno = 0; } } } // ============================================================ // Процедура распечатывает имена и другую информацию о серверах // ============================================================ void SLIST::PrintServersName(void) { // Цикл по всем обнаруженным в сети активным серверам for(int i=0; i<MAX_SERVERS; i++) { if(ServerName[i][0]) { // Выводим имя сервера printf("%s",ServerInfo[i].serverName); // Если номер канала текущего сервера совпадает с // номером канала первичного сервера, выводим строку "\t[Primary]" if(ConnID[i] == PrimaryConnID) printf("\t[Primary]"); else printf("\t[ ]"); // Если номер канала текущего сервера совпадает с // номером канала сервера по умолчанию, выводим строку " [Default]" if(ConnID[i] == DefaultConnID) printf(" [Default]"); else printf(" [ ]"); // Выводим версию сетевой операционной системы, // работающей на текущем сервере printf(" v.%d.%d, ", ServerInfo[i].netwareVersion, ServerInfo[i].netwareSubVersion); // Для подключенных серверов выводим серийный // номер и номер приложения if(SerialNumber[i] != 0L) printf("s/n %08.8lX/%04.4X", SerialNumber[i], ApplicationNumber[i]); else printf("- Not Logged In -"); // Выводим номер канала, используемого для связи с текущим сервером printf("\tConnID: %d,",ConnID[i]); // Выводим максимальное число каналов, поддерживаемых // сервером, и количество используемых каналов printf(" (%d-%d)\n", ServerInfo[i].maxConnectionsSupported, ServerInfo[i].connectionsInUse); } } } Файл sap.hpp содержит все определения констант и описания структур, необходимые для программы SLIST. В частности, в этом файле описан класс SLIST. // =================================================== // Листинг 5. Include-файл для программы SLIST.CPP // Файл slist!\sap.hpp // // (C) A. Frolov, 1993 // =================================================== // Максимальное количество серверов, для которых выполняется опрос #define MAX_SERVERS 8 // Типы сервиса SAP #define GENERAL_SERVICE 1 #define NEAREST_SERVICE 3 #define NOT_SUPPORTED 1 // Короткоживущий сокет #define SHORT_LIVED 0x00 // Сокет для SAP-протокола #define SAP_SOCKET 0x452 // Тип пакета SAP #define SAP_PACKET_TYPE 2 // Определения используемых типов данных #define BYTE unsigned char #define WORD unsigned short // Сетевой адрес typedef struct IPXAddress { BYTE network[4]; BYTE node[6]; WORD socket; } IPXAddress; // Заголовок IPX-пакета typedef struct IPXHeader { WORD checkSum; WORD length; BYTE transportControl; BYTE packetType; IPXAddress destination; IPXAddress source; } IPXHeader; // Заголовок SAP-пакета typedef struct SAPHeader { WORD checksum; WORD length; BYTE transportControl; BYTE packetType; IPXAddress destination; IPXAddress source; WORD SAPPacketType; WORD serverType; BYTE serverName[48]; IPXAddress serverAddress; WORD interveningNetworks; } SAPHeader; // Пакет для посылки SAP-запроса typedef struct SAPQueryPacket { WORD checksum; WORD length; BYTE transportControl; BYTE packetType; IPXAddress destination; IPXAddress source; WORD queryType; WORD serverType; } SAPQueryPacket; // Структуры для описания блока ECB typedef struct ECBFragment { void far *address; WORD size; } ECBFragment; typedef struct ECB { void far *linkAddress; void (far *ESRAddress)(); BYTE inUseFlag; BYTE completionCode; WORD socketNumber; BYTE IPXWorkspace[4]; BYTE driverWorkspace[12]; BYTE immediateAddress[6]; WORD fragmentCount; ECBFragment fragmentDescriptor[2]; } ECB; // SAP-пакет typedef struct { IPXHeader Header; WORD ResponseType; WORD ServerType; BYTE ServerName[48]; BYTE Network[4]; BYTE Node[6]; WORD Socket; WORD InterveningNetworks; } SAP; // Структура для передачи SAP-пакета typedef struct { ECB theECB; SAPQueryPacket SAPq; } SEND_PACKET; // Структура для приема SAP-пакета typedef struct { ECB theECB; SAP SB; } RECEIVE_PACKET; // Информация о файл-сервере typedef struct { char serverName[48]; BYTE netwareVersion; BYTE netwareSubVersion; WORD maxConnectionsSupported; WORD connectionsInUse; WORD maxVolumesSupported; BYTE revisionLevel; BYTE SFTLevel; BYTE TTSLevel; WORD peakConnectionsUsed; BYTE accountingVersion; BYTE VAPversion; BYTE queingVersion; BYTE printServerVersion; BYTE virtualConsoleVersion; BYTE securityRestrictionLevel; BYTE internetBridgeSupport; } FILE_SERV_INFO; // Описания функций библиотеки NetWare C Interface extern "C" int IPXInitialize(void); extern "C" int IPXOpenSocket(WORD *, BYTE); extern "C" int IPXListenForPacket(ECB *); extern "C" int IPXCancelEvent(ECB *); extern "C" int IPXCloseSocket(WORD); extern "C" WORD IntSwap(WORD); extern "C" void IPXGetInternetworkAddress(BYTE *); extern "C" void IPXSendPacket(ECB *); extern "C" void IPXRelinquishControl(void); extern "C" IPXGetLocalTarget(BYTE *, BYTE *, int*); extern "C" WORD IPXGetIntervalMarker(void); extern "C" long LongSwap(long); extern "C" int AttachToFileServer(char *, WORD *); extern "C" int SetPrimaryConnectionID(int); extern "C" int GetServerInformation(int, FILE_SERV_INFO *); extern "C" WORD GetPreferredConnectionID(void); extern "C" WORD GetPrimaryConnectionID(void); extern "C" WORD GetDefaultConnectionID(void); extern "C" int SetPreferredConnectionID(WORD); extern "C" int GetConnectionID(char *, WORD *); extern "C" void DetachFromFileServer(WORD); extern "C" int GetNetWareShellVersion(BYTE *,BYTE *, BYTE *); extern "C" int IsConnectionIDInUse(WORD); extern "C" int GetNetworkSerialNumber(long *, int*); // Класс SLIST class SLIST { private: WORD QueryType; // тип запроса WORD SrcSocket; // сокет // Массив для приема SAP-пакетов RECEIVE_PACKET Query[MAX_SERVERS]; // Передаваемый SAP-пакет SEND_PACKET SendPacket; // Таблицы имен файл-серверов, серийных // номеров и номеров приложений char ServerName[MAX_SERVERS][48]; long SerialNumber[MAX_SERVERS]; int ApplicationNumber[MAX_SERVERS]; // Таблица информации о файл-серверах FILE_SERV_INFO ServerInfo[MAX_SERVERS]; // Таблица номеров каналов файл-серверов WORD ConnID[MAX_SERVERS]; // Функции для приема и передачи SAP-пакетов void ReceiveSAPPacket(RECEIVE_PACKET *Query); void SendSAPPacket(void); // Функции для получения имен файл-серверов и // другой информации о файл-серверах void GetServersName(void); void GetServersInfo(void); public: int errno; // код ошибки WORD PreferredConnID; // предпочтительный сервер WORD PrimaryConnID; // первичный сервер WORD DefaultConnID; // сервер по умолчанию BYTE MajorVersion; // верхний номер версии BYTE MinorVersion; // нижний номер версии BYTE Revision; // номер изменений SLIST(int); // конструктор ~SLIST(); // деструктор // Функция для вывода имен серверов void PrintServersName(void); // Проверка ошибок int Error(void) { return errno; } }; 2.4.2. Пограмма LOGВ этом разделе мы приведем исходный текст программы, выполняющей подключение пользователя к файл-серверу. Возможности этой программы ограничены по сравнению со стандартной утилитой login.exe: она, например, не выполняет интерпретацию файлов Login Script и System Login Script. После подключения к файл-серверу диск "S:" рабочей станции отображается на том SYS:. Вы можете использовать нашу программу как прототип собственной процедуры подключения к файл-серверу. После проверки присутствия сетевой оболочки программа LOG с помощью функции GetConnectionNumber() получает номер канала текущего файл-сервера и затем, вызвав функцию GetFileServerName(), определяет имя текущего файл-сервера. Имя и номер канала текущего сервера выводятся в стандартный поток вывода. Далее программа запрашивает имя сервера, имя пользователя и его пароль, при помощи функции AttachToFileServer() создает канал с указанным файл-сервером. Если канал уже есть или его удалось создать, новый сервер делается предпочтительным, для чего вызывается функция SetPreferredConnectionID(). Затем вызывается функция LoginToFileServer(). Она пытается подключить пользователя к предпочтительному серверу. После подключения программа с помощью функции CheckConsolePrivileges() проверяет, имеет ли данный пользователь права оператора консоли, и выводит соответствующее сообщение. Для того чтобы получить информацию о сервере, к которому только что подключился пользователь, программа LOG вызывает функцию GetFileServerDescriptionStrings(), которая записывает в четыре буфера имя фирмы-изготовителя, изменения и дату изменений, права на сетевую операционную систему. Содержимое всех этих буферов выводится в стандартный поток вывода. Затем вызывается функция GetServerInformation(). С ее помощью определяется максимальное количество пользователей для данного сервера. Так как мы только что подключились к файл-серверу, он должен стать первичным, поэтому на следующем шаге программа LOG вызывает функцию SetPrimaryConnectionID() и делает новый сервер первичным. Подключившись к файл-серверу, вы еще не имеете доступа к его томам. Для того чтобы вы могли работать с дисками файл-сервера, вам необходимо отобразить один или несколько локальных дисков на сетевые каталоги. В нашей программе мы отображаем диск "S:" на корневой каталог тома SYS: нового первичного сервера. Для этого мы вызываем функцию AllocPermanentDirectoryHandle(). Эту функцию, а также все, что связано с дисками сервера, мы рассмотрим в следующей главе. // =================================================== // Листинг 6. Подключение к серверу // Файл log\log.c // // (C) A. Frolov, 1993 // =================================================== #include <stdlib.h> #include <stdio.h> #include <string.h> #include "nit.h" // include-файлы из библиоткеи #include "niterror.h" // NetWare C Interface // Эта функция не описана в include-файлах // библиотеки NetWare C Interface, поэтому опишем ее сами. void GetServerInformation(int, FILE_SERV_INFO*); void main(void) { int ccode; char ServerName[48]; char UserName[48]; char Password[128]; WORD ConnID, ConnNumber; char companyName[80], revision[80]; char revisionDate[24], copyrightNotice[80]; FILE_SERV_INFO serverInfo; BYTE newDirectoryHandle, effectiveRightsMask; char driveLetter; char MajorVersion=0; char MinorVersion=0; char Revision=0; printf("NetWare Login, (C) Фролов А.В., 1993\n"); asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); return; } // Получаем номер канала, используемого сервером // по умолчанию (default) для связи с рабочей станцией, на // которой была запущена эта программа ConnNumber = GetConnectionNumber(); // Получаем имя файл-сервера, используемого по умолчанию (default) GetFileServerName(0, ServerName); // Выводим имя и номер канала для // сервера, используемого по умолчанию if(ConnNumber) printf("Сервер по умолчанию '%s', ConnNumber=%04.4X\n", ServerName, ConnNumber); // Вводим имя сервера, имя пользователя и пароль. // Преобразуем все введенные буквы в заглавные. printf("\nВведите имя сервера: "); gets(ServerName); strupr(ServerName); printf("\nВведите ваше имя: "); gets(UserName); strupr(UserName); printf("\nВведите пароль: "); gets(Password); strupr(Password); // Создаем канал с сервером ccode = AttachToFileServer(ServerName, &ConnID); // Если канал удалось создать или он уже был создан раньше, // выводим имя сервера и номер канала, используемого // на рабочей станции для идентификации сервера. if(ccode == 0 || ccode == ALREADY_ATTACHED_TO_SERVER) { printf("\nServerName='%s', ServerID=%04.4X", ServerName, ConnID); // Делаем данный сервер предпочтительным для того, // чтобы все запросы направлялись к нему в первую очередь SetPreferredConnectionID(ConnID); // Подключаем пользователя к файл-серверу ccode = LoginToFileServer(UserName,OT_USER,Password); if(!ccode) { // Если подключение произошло успешно, проверяем, есть ли // у подключившегося пользователя права оператора консоли if(!CheckConsolePrivileges()) printf("Вы оператор консоли\n"); // Получаем строки описания сервера и выводим их // в стандартный поток GetFileServerDescriptionStrings(companyName, revision, revisionDate, copyrightNotice); printf("Описание сервера:\n%s\n%s\n\n%s\n%s\n", companyName,revision, revisionDate, copyrightNotice); // Получаем информацию о сервере, выводим максимальное количество // пользователей, которые могут подключиться к // данному файл-серверу. GetServerInformation(sizeof(serverInfo), &serverInfo); printf("Версия на %d пользователей\n", serverInfo.maxConnectionsSupported); // Делаем данный сервер первичным. SetPrimaryConnectionID(ConnID); // Отображаем диск S: рабочей станции на // корневой каталог тома SYS: сервера driveLetter = 'S'; ccode = AllocPermanentDirectoryHandle(0,"SYS:\\", driveLetter, &newDirectoryHandle,&effectiveRightsMask); printf("Диск отображен, код CCode = %d\n",ccode); } } else { printf("Ошибка при подключении: %04.4X\n",ccode); return; } } |