автоподключение USBIP устройств на windows-сервере
Поднимаю избитую, изжеванную, но так толком и не освещенную в интернете тему автоматического подключения проброшенных посредством USBIP устройств, в особенности — капризных принтеров. Тут уже и технология Spice на подходе, а виндовый драйвер usbip все еще нулевой версии (0.2), и ни гуёв тебе, ни автоматизации, ни уверенности в отсутствии завтрашнего BSOD’а никакой. Поэтому, по беглому напутствию с форумов «пропиши скриптом автоподключение» приходится извращаться, дабы превратить из бесплатного решения некоторое подобие платного (USB over IP, например, где есть все от блекджека до сами знаете чегокого).
В большинстве случаев проще вообще не заморачиваться usbip для проброса принтеров на терминальный сервер, а использовать возможности штатного клиента сервера терминалов. Но данный подход обязывает устанавливать драйвера для принтера на клиентскую станцию. Все хорошо и понятно, но нужно учитывать тот факт, что принтеров в сети может быть огромное количество, в том числе и злосчастный Canon, который, мягко говоря, забил большой жирный болт на всяких там пингвинов и чёртиков. Также ситуация может усугубляться тем, что системы на клиенте вообще никакой может и не быть, и даже жесткого диска, и вообще, образ линукса грузится по сети, при чем образ один для всех. В таком случае, мало того, что все необходимые драйвера должны быть установлены на самом сервере терминалов, так нам еще придется создавать нагромождение глюковдрайверов уже в образе линукса, что будет крутится на тонких клиентах. Учитывая, что принтеров много, и вряд ли получится подружить всех их с линуксом, а также то, что все-равно придется как-то расшаривать всякие там флешки, токены/смарткарты для клиент-банкинга, донглы для всяких там Лиг, и видео-наблюдений я все-же остановился на пробросе устройств посредством USBIP.
Но бесплатный сыр сами знаете где. Хотя будь я даже директором крупного холдинга, платить по 100 вечнозеленых за проброс каждой usb-фенечки вряд ли бы согласился, не то что текущее руководство, которое хочет все и сразу, и обязательно забесплатно.
Особо нетерпеливым можно перейти сразу к итогам статьи и не читать всю эту бредятину, которую я здесь написал, но все же рекомендую прочитать статью целиком для полного понимания происходящего.
Итак, согласно инструкции все просто прекрасно — даем команду:
usbip -a [IP_машины] [идентификатор устройства, присвоенный на той стороне]
и радуемся появившемуся на сервере устройству, будто оно было подключено локально. В лабораторных условиях все чудесно, но суровая реальность работы инфраструктуры на тонких клиентах состоит в том, что пользователи приходят и уходят со своего рабочего места, включают и выключают свой компьютер, и по хорошему хотелось бы, чтобы принтеры (идти будет речь в основном про них, т.к. с остальными устройствами больших проблем не возникает) появлялись при включении очередного компьютера и сами чудесным образом исчезали, когда компьютер выключается. Вот тут-то и начинаются проблемы.
Проблема №1. Если связь с usbip-сервером (машиной, на которой расшарено устройство) была ВНЕЗАПНО прервана, т.е. тонкий клиент могли просто выключить — то usbip-клиент виснет намертво, и хорошо, если он был просто запущен из командной строки, можно нажать CTRL+C. А если он был запущен из скрипта, да еще от имени системной учетной записи, т.к. скрипт запущен как служба? Вроде бы решение назревает само-собой — все время пингуем станцию, если пинг пропадает — убиваем процесс usbip, вот как описано здесь. Но как оказалось, не все так просто. Во первых, решение, описанное по предыдущей ссылке у меня адекватно не заработало при проброшенном десятке устройств, собственно из-за чего и пишется данная статья. Во-вторых, если убить процесс usbip из скрипта, то его бездыханная тушка еще долго будет конвульсивно трепыхаться в памяти, прежде чем действительно завершится. Именно завершать процессы usbip я решил по причине того, что если связи с тонким клиентом все также нету, то у меня usbip при подключении вис, не помню уже точно, но вроде надолго.
Решение было принято кардинальное — в цикле все так же пингуем станцию, если нет связи, убиваем процесс usbip, но убиваем хитро, а именно закрываем его хэндлы. Сначала хэндл \Device\0000003a, после чего процесс завершается если уже был произведен проброс устройства. Также закрываем хэндл \Device\Afd в таком случае процесс завершится гарантированно, даже если он в этот момент просто ломился на тонкий клиент. С запуском usbip с параметром -d я морочиться не стал, ибо непонятно, как себя поведет USBIP в том случае, если у нас проброшено несколько устройств с одинаковым идентификатором. Итак нам понадобятся такие утилиты от Марка Руссиновича:
- Handle (http://technet.microsoft.com/ru-ru/sysinternals/bb896655)
- Process Explorer (http://technet.microsoft.com/ru-ru/sysinternals/bb896653)
Название утилиты handle говорит само за себя, она нужна нам для того, чтобы управлять дескрипторами приложения. А вот Process Explorer нужен для того, чтобы наглядно посмотреть номера нужных нам дескрипторов (номера в каждой системе вроде как отличаются) да и просто для того, чтобы было удобно наблюдать за процессом работы скриптов. Но прошу обратить внимание на то, что драйвер Process Explorer’a procexp141.sys свалил мой сервер терминалов в BSOD, благо, это было в нерабочее время. Так что настоятельно рекомендую крайне осторожно устанавливать какие-либо драйвера, утилиты и тому подобное на сервер, в котором работают десятки пользователей и отказоустойчивость которого должна быть превыше всего.
UPD 19.10.11. В процессе использования обнаружилось, что синий экран появляется все-таки из-за утилиты handle (или от совокупности handle и process explorer, при отображении в последнем открытых дескрипторов) в том случае если скрипт работает несколько суток подряд. На данный момент активно ищется решение данной проблемы. Возможными решениями являются включение файла подкачки а также запуск скриптов только в нужное нам время. В любом случае — следите за обновлениями статьи.
Также, в случае, если сервер терминалов у нас не англоязычный, нужно найти в сети, или скачать из подвала статьи утилиту ping, что идет в составе любой англоязычной Windows. Обрабатывать вывод такой утилиты намного проще, нежели русскоязычной.
Итак, создадим скрипт с условным названием _pinger.vbs (подчеркивание нужно для выразительности из сотни скриптов, об этом позже) и таким содержимым:
Do While True for count = 1 to 100 wshell.Run "cmd /c c:\USBIP\ping.exe 192.168.0." & count & " _ -n 1 -l 1 -4 -w 1 > pinger.output", 1, True if objFSO.FileExists("c:\USBIP\pinger.output") then set f = objFSO.GetFile("c:\USBIP\pinger.output") set ts = f.OpenAsTextStream(ForReading, 0) Do While ts.AtEndOfStream <> True TextLine = ts.ReadLine if Instr(TextLine, "Lost = 1") then Set colProcessList = objWMIService.ExecQuery ("SELECT * FROM _ Win32_Process WHERE Name = 'usbip.exe' AND CommandLine LIKE '%192.168.0." & count & " %'") For Each objProcess in colProcessList wshell.Run "handle -p " & objProcess.ProcessId & " -c 128 -y", 1, True '\Device\0000003a wshell.Run "handle -p " & objProcess.ProcessId & " -c EC -y", 1, True '\Device\Afd Next end if Loop ts.Close end if WScript.Sleep 50 next 'WScript.Sleep 1000 Loop
Итак, в бесконечном цикле «Do While True … Loop» выполняем такие команды:
1. Цикл от одного до 100, что будет означать перебор всех IP-адресов 192.168.0.1 до 192.168.0.100.
2. Далее выполняем пинг каждого адреса с параметрами ‘-n 1 -l 1 -4 -w 1’ и выводом в файл pinger.output, который мы будем далее парсить. Конструкцию предваряет «cmd /c». Без этой конструкции не будет работать команда вывода в файл «… > file.txt». Параметры ping означают, что число отправляемых запросов будет равно одному (-n 1), размер буфера также будет равен единице (-l 1), используемым протоколом будет IPv4 (-4), и, наконец, минимальный таймаут, в 1 миллисекунду (-w 1). Такие параметры позволят нам сэкономить время на определении, есть ли станция на связи, или нет. Конечно, данный подход требует безупречного функционирования локальной сети.
3. После этого открываем для чтения файл с результатами пинга станции, и ищем там конструкцию Lost = 1, что подразумевает отсутствие станции на связи. Если это произошло, то ищем процесс «usbip.exe» запущенный с параметром в виде нужного нам IP-адреса, узнаем его process-id и передаем этот параметр утилите handle, которая и начнет закрывать дескрипторы данного процесса, что приведет к его закрытию. Не ошибитесь при построении запроса запущенных процессов. В конструкции
...AND CommandLine LIKE '%192.168.0." & count & " %'"
важен пробел перед закрывающим процентом %, который означает инструкцию «все остальное не имеет значения». Без этого пробела поиск процесса, запущенного с параметрами 192.168.0.1 найдет также и 192.168.0.10, 192.168.0.11 и т. д.
Здесь, и далее по статье конструкция wshell.Run запускается с параметрами «1, True», что означает: стандартно отобразить окно приложения и ждать его завершения, это не даст скрипту обрабатывать последующие команды до завершения текущей.
Проблема №2. Данная проблема разветвляется сразу на три: во-первых: если удалить неактивный принтер, после того, как тонкий клиент был выключен, то при следующем переподключении некоторые принтеры уже не появляются сами по себе и нужно вручную зайти в Диспетчер устройств и нажать там кнопку «Обновить конфигурацию оборудования». Во вторых: в некоторых случаях не помогает даже обновление конфигурации в Диспетчере устройств. В таком случае нужно вручную удалить устройство «Устройство поддержки USB-принтера» и обновить конфигурацию оборудования, или же устанавливать принтер вручную, вручную указав действительный порт USB0xx. В третьих: порядок подключения устройств к серверу нам заранее не известен, а драйвер usbip вешает каждое устройство на любой свободный порт, который нумеруется как USB001, USB002 и так далее. Таким образом сейчас у нас принтер может висеть на порту USB002, а завтра уже на порту USB006. Соответственно, даже если не удалять неактивный принтер, то в случае изменения номера порта будет создан новый принтер, с таким же названием, но уже активный и пригодный для работы. Количество вновь создаваемых принтеров будет возрастать пропорционально количеству всех проброшенных USB-устройств.
Решил я эту проблему таким образом: в цикле подключаем устройство командой usbip -a [ip] [id] и ждем ее завершения, после чего удаляем все задания печати для данного принтера, удаляем сам принтер и удаляем ветку реестра, которая характеризует устройство поддержки USB-принтера. Если же есть проблемные принтеры, которые не устанавливаются автоматически как обычные PnP-устройства, то параллельным скриптом в цикле сканируем наличие в системе устройства с именем принтера, и, если таковое имеется — устанавливаем его посредством командной строки с помощью команды «rundll32 printui.dll,PrintUIEntry …»
Для реализации задуманного нам понадобятся такие утилиты:
- Devcon (http://support.microsoft.com/kb/311272/ru)
- Regperm (офсайт лежит, поэтому качаем отсюда: http://www.softpedia.com/get/Tweak/Registry-Tweak/Regperm.shtml)
Утилита Devcon нам понадобится для выполнения функций Диспетчера устройств, только из командной строки. Так как ветку реестра, отвечающую за устройства нам просто так не удалить, не дав соответствующие права нужным пользователям — используем утилиту Regperm, с помощью которой можно из командной строки установить необходимые права на любую ветку реестра.
Текст скрипта, который автоматически подключает принтер, и, если он был отключен, чистит реестр и удаляет сам принтер:
Const HKEY_CLASSES_ROOT = &H80000000 Const HKEY_CURRENT_USER = &H80000001 Const HKEY_LOCAL_MACHINE = &H80000002 Const HKEY_USERS = &H80000003 strComputer = "." Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set objFSO = CreateObject("Scripting.FileSystemObject") set wshell = WScript.CreateObject("Wscript.Shell") Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv") wshell.CurrentDirectory = "C:\USBIP" Do While True wshell.Run "usbip -a 192.168.0.16 2-1", 1, True Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer") For Each objPrinter in colInstalledPrinters if Instr(objPrinter.Name, "сервисный отдел") then objPrinter.CancelAllJobs() objPrinter.Delete_ strKeyPath = "SYSTEM\CurrentControlSet\Enum\USBPRINT\SamsungML-1210" wshell.Run "regperm.exe /A:Администраторы:F /K " & chr(34) & "HKEY_LOCAL_MACHINE\" _ & strKeyPath & chr(34) & " /f /i", 1, True DeleteSubkeys HKEY_LOCAL_MACHINE, strKeypath end if Next WScript.Sleep 4000 Loop Sub DeleteSubkeys(HKEY_LOCAL_MACHINE, strKeyPath) objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys If IsArray(arrSubkeys) Then For Each strSubkey In arrSubkeys DeleteSubkeys HKEY_LOCAL_MACHINE, strKeyPath & "\" & strSubkey Next End If objRegistry.DeleteKey HKEY_LOCAL_MACHINE, strKeyPath End Sub
Тут все также в бесконечном цикле пытаемся подключить устройство, а после его отключения ищем принтер с названием, которое выделяет его среди всех остальных. В данном примере — «сервисный отдел». Методами CancelAllJobs, и Delete_ (подчеркивание обязательно) очищаем очередь печати и удаляем принтер. Далее, изменяем разрешения на ветку реестра описывающую устройство USB-принтера, которая обычно названа именем принтера и находится по пути HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBPRINT\, а именно — даем группе Администраторы полные права командой «regperm /A:Администраторы:F … /f /i», где параметр «/f» означает присвоение аналогичных разрешений дочерним веткам, а параметр «/i» указывает на дополнительное наследование прав от родительских веток, дабы не удалить права для системной учетной записи «SYSTEM». После этого рекурсивно удаляем данную ветку. Здесь кроется большая проблема, которую я пока так и не смог решить — мы удаляем устройства всех принтеров с таким названием, что создаст проблемы, если в сети есть принтеры с одинаковыми названиями. Буду признателен, если кто-нибудь выскажет свои идеи на этот счет. Отмечу также, что если мы дали права изменение ветки группе администраторов, то и запуск скрипта (или службы, об этом ниже) должен производится от имени администратора.
Если мы знаем, что принтер сам по себе не устанавливается, то рисуем еще один скрипт, который будет выполнятся параллельно предыдущему и при наличии соответствующего устройства будет устанавливать принтер:
Const ForReading = 1, ForWriting = 2, ForAppending = 3 strComputer = "." Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set objFSO = CreateObject("Scripting.FileSystemObject") set wshell = WScript.CreateObject("Wscript.Shell") wshell.CurrentDirectory = "C:\USBIP" Do While True Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer where _ Name = 'сервисный отдел'") if colInstalledPrinters.Count > 0 then WScript.Sleep 10000 else wshell.Run "cmd /c devcon findall *SamsungML-1210* > c:\usbip\ivanov.p.p.output" if objFSO.FileExists("c:\usbip\ivanov.p.p.output") then set f = objFSO.GetFile("c:\usbip\ivanov.p.p.output") set ts = f.OpenAsTextStream(ForReading, 0) Do While ts.AtEndOfStream <> True TextLine = ts.ReadLine if Instr(TextLine, "USBPRINT\SAMSUNGML-1210") then PrtName = mid(TextLine, Instr(TextLine, "USB0"), 6) wshell.Run "rundll32 printui.dll,PrintUIEntry /if /z /b " & chr(34) & "сервисный отдел" _ & chr(34) & " /u /r " & chr(34) & PrtName & chr(34) & " /f " & chr(34) _ & "%windir%\ML-1210\SPLV1.inf" & chr(34) & " /m " & chr(34) _ & "Samsung ML-1200 Series" & chr(34), 1, True exit do end if Loop ts.Close end if WScript.Sleep 5000 end if Loop
В этом скрипте мы проверяем наличие нужного на принтера, если он есть — делаем небольшую паузу и начинаем все заново, если нету — проверяем наличие нужного устройства. Наличие устройства будем проверять утилитой devcon, которая укажет нам порт, на котором висит принтер, будь то USB001 или USB084. Порт нам нужен для того, чтобы вручную, с помощью командной строки установить принтер. В этом есть своя положительная сторона — принтер мы создаем сразу с нужными драйверами и нужным, удобочитаемым названием. Итак, по порядку.
Сначала выполняем команду «devcon findall *SamsungML-1210*», вывод которой направляем в файл ivanov.p.p.output Тут следует отметить, что названия скриптам, и результирующим файлам я давал в соответствии с доменными логинами, для более легкого ориентирования в большом количестве скриптов а также предварял подчеркиванием общие скрипты типа _pinger.vbs, _set_online_printers.vbs и прочих. Если устройство найдено, то в файле ivanov.p.p.output появится строка следующего вида:
USBPRINT\SAMSUNGML-1210\2&2451A4D6&0&USB006 : SamsungML-1210
где USB006 и есть порт, на котором сейчас висит принтер. Командой
mid(TextLine, Instr( TextLine, "USB0" ), 6)
вырезаем это название из строки и после этого уже даем команду на установку принтера ‘rundll32 printui.dll,PrintUIEntry /if /z /b «сервисный отдел» /u /r USB006 /f «%windir%\ML-1210\SPLV1.inf» /m «Samsung ML-1200 Series»‘ где параметр /if указывает на то, что будет использоваться конкретный *.inf файл, параметр /z указывает на то, что давать общий доступ принтеру не нужно, /b задает имя принтера, /u означает использование уже существующего драйвера, если таковой имеется. Это поможет нам в том случае, если драйвер не подписанный, и из скрипта принтер не установится, даже при отключенном предупреждении о подписях драйверов. Поэтому нужно один раз установить принтер вручную, подтвердить использование не подписанного драйвера, а уже в дальнейшем установка будет проходить без вопросов. Параметр /r задает порт, который мы определили ранее, /f указывает путь к *.inf-файлу, и, наконец, параметр /m указывает модель принтера, по которой будет вестись поиск в *.inf-файле. Отмечу, что параметры данной команды регистрозависимы, так что будьте внимательны! Подробнее о возможных параметрах можно почитать здесь.
Как я уже сказал, с подключением других устройств обычно проблем не возникает, и для их работы достаточно запуска скриптов такого вида:
set wshell = WScript.CreateObject("Wscript.Shell") wshell.CurrentDirectory = "C:\USBIP" Do While True wshell.Run "usbip -a 192.168.0.3 1-4", 1, True WScript.Sleep 1000 Loop
Для идеальной работы нам еще понадобятся такие скрипты:
- скрипт переименования принтеров в удобочитаемые названия. Этот скрипт предназначен для самоустанавливающихся при подключении принтеров:
strComputer = "." Do While True Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set objFSO = CreateObject("Scripting.FileSystemObject") Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer") For Each objPrinter in colInstalledPrinters if Instr(objPrinter.Name, "1606") then objPrinter.RenamePrinter "бухгалтерия" end if if Instr(objPrinter.Name, "1510") then objPrinter.RenamePrinter "инженеры" end if Next WScript.Sleep 1000 Loop
Set Shell = CreateObject("Shell.Application") Do While True Set objFolder = Shell.NameSpace(4) For each printer in objFolder.Items if Instr(printer, "бухгалтерия") then If objFolder.GetDetailsOf(printer, 2)="Не подключен" then printer.InvokeVerbEx("&Использовать принтер в оперативном режиме") end if end if Next WScript.Sleep 60000 Loop
Тут следует отметить, что текст скрипта будет отличаться в разных локализациях операционной системы.
set wshell = WScript.CreateObject("Wscript.Shell") wshell.CurrentDirectory = "C:\USBIP" wshell.Run "_pinger.vbs" wshell.Run "_rename_printers.vbs" wshell.Run "_set_online_printers.vbs" wshell.Run "petrenko.e.s.vbs" wshell.Run "vasilenko.e.a.1.vbs" wshell.Run "vasilenko.e.a.2.vbs" wshell.Run "ivanenko.l.d.vbs" wshell.Run "adminenko.s.e.1.vbs" wshell.Run "adminenko.s.e.1.1.vbs" wshell.Run "directorenko.a.v.1.vbs" wshell.Run "directorenko.a.v.1.1.vbs"
Данный скрипт будет запускаться в качестве службы. Для этого нам понадобятся такие утилиты, входящие в состав Windows Resource Kits:
- srvany
- instsrv
Для запуска скрипта как сервиса нам необходимо скопировать утилиту srvany в папку c:\WINDOWS\system32\ и выполнить команду «instsrv.exe USBIP c:\WINDOWS\system32\srvany.exe». Эта команда создаст службу с названием USBIP. После этого нам необходимо прописать, что конкретно эта служба будет запускать. Для этого идем в реестр по пути HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBIP и добавляем раздел «Parameters», в котором добавляем REG_SZ-параметр с именем «Application» и значением «cscript c:\usbip\_start.vbs», где _start.vbs и есть название нашего инициализирующего скрипта. В данном примере также нужно зайти в свойства данной службы и назначить ее запуск от имени Администратора, т.к. права на модифицирование веток реестра мы даем группе администраторов. Также, для того, чтобы утилита handle запустилась под системной учетной записью, нам нужно «принять лицензионное соглашение» под ней. Но так как само окно с лицензионным соглашением мы не увидим, даже запустив процесс под системной учеткой, мы поступим проще, а именно создадим в реестре такой ключ «HKEY_USERS\.DEFAULT\Software\Sysinternals\Handle» с DWORD-параметром EulaAccepted и значением 1.
Также советую установить на запуск каждому пользователю вот такой небольшой, но не менее полезный скрипт, который делает ближайший принтер принтером по-умолчанию:
On Error Resume Next strComputer = "." WScript.Sleep 5000 Do While True Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set objFSO = CreateObject("Scripting.FileSystemObject") Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer") For Each objPrinter in colInstalledPrinters if Instr(objPrinter.Name, "менеджеры") then objPrinter.SetDefaultPrinter end if Next WScript.Sleep 10000 Loop
Обращаю внимание на конструкцию On Error Resume Next, которая указывает серверу сценариев не обрабатывать ошибки и продолжать работу скрипта в случае возникновения оных. Дело в том, что команда objPrinter.SetDefaultPrinter не всегда отрабатывает корректно, поэтому, будем повторять попытку установки принтера по-умолчанию даже в случае ошибки.
Также хочу заметить, что во время всей этой эпопеи я столкнулся с тем, что под некоторые устройства начисто отсутствуют драйвера под x64 систему. Для этого пришлось параллельно подымать домен (виртуальную машину в терминологии гипервизора Xen) с Windows XP Pro SP3 на борту. Но имейте в виду, что лично у меня под Windows XP usbip.exe второй версии категорически отказался запускаться. Помог только откат до версии v0.1, при чем достаточно заменить только исполняемый файл.
Еще настоятельно рекомендую проводить установку USBIP и скриптов в каталог c:\USBIP, т.е. поближе к корню, и с кратчайшим названием.
Преимуществом использования vbs-скриптов перед auto-it программированием в данном случае является меньшее потребление памяти. В нашем случае выделяется память только под процессы usbip, сам процесс скрипта занимает намного меньше памяти нежели скомпилированный auto-it скрипт. Данное преимущество становится ключевым при подключении большого количества устройств. Также в случае с vbs-скриптами все прозрачно и понятно, и при необходимости быстро модифицируется.
Ну что ж, подытожим. Для корректной работы всей связки нам нужны следующие утилиты:
- devcon
- handle
- ping
- regperm
- instsrv
- srvany
- и, собственно сам usbip
Необходимые скрипты:
- _start.vbs — инициализирующий скрипт
- _set_online_printers.vbs — скрипт, устанавливающий оперативный режим LPT-принтерам
- _rename_printers.vbs — скрипт, переименовывающий принтера в удобоваримые названия
- _pinger.vbs — средство для контроля за наличием рабочих станций на связи
- пользователь1.1.vbs — пример скрипта подключения обычного usb-устройства
- пользователь1.2.vbs — пример скрипта подключения второго usb-устройства
- пользователь2.1.vbs — пример скрипта подключения обычного принтера и последующего удаления его следов при отключении
- пользователь3.1.vbs — пример скрипта подключения проблемного принтера, который не устанавливается автоматически
- пользователь3.1.1.vbs — пример скрипта, который отслеживает подключение устройства проблемного принтера и устанавливает его
скачать одним архивом: scripts.zip (645 Загрузок)
Вот такой вот монструозный скрипт получился, но тем не менее работающий уже как 2 недели в энтерпрайзе. Напоминаю, есть нерешенная проблема с одинаковыми принтерами. Буду рад любым замечаниям и предложениям или более изящным решениям.
Спасибо, Товарищ. Хороший выход нашли из положения. Еще учитывая что это работает в режиме нон стоп то просто Браво. Я сейчас занимаюсь подобным маразмом. Огромная проблема с глухим зависанием утилиты.
Всегда пожалуйста! В дальнейшем хотелось бы узнать Ваш результат внедрения usbip в энтерпрайз.
Ну вот уже 4 месяц все работает таким образом. На трминале под Ubuntu работает скрипт, который отслеживает вывод usbip —l. Если там отсутствует строка 4-2:1.0 -> usbip , то заново выполняем который выполняет тупо выполняет сначала комманду отключения девайса и подключения (usbip -d 1 и -a) и остается в бесконечном цикле пока USBIP выполняется. При его завершении перезапускается скрипт. Проблемы: иногда устройство на виндовс машине монтируется в 2 порт и никакой «деататч» не помогает 2 — проблема: иногда при отключении устройства зависает UsbIP в терминале и приходится убивать демон — процесс. Я с линуксами дружу крайне мало и со скриптовіми язіками знаком слабо. Вот опять сел искать решения
спасибо за репорт! подтверждаю. У меня также все это работает в энтерпрайзе с сентября 2011 — уже 9 месяцев nonstop, на нескольких серверах. Проблемы с выпадами в bsod из-за частого закрытия дескрипторов я решил пока что в лоб, а именно — убрал из сценария утилиту handle вообще. В итоге, если устройство отпало — через некоторое время процесс usbip отваливается сам по себе. Хотел было разработать свой аналог утилиты, которая более аккуратно (с меньшими телодвижениями) закрывала бы хэндлы висящего usbip, но уперся тогда в некоторые особенности разработки под x64-архитектуру. Теперь ни времени ни желания продолжать нету. Также оказалось, что драйвер одного из принтеров под win2003 x64 был сильно глючным, именно из-за него чаще всего сервер терминалов и выпадал в синий экран. Решил проблему вынесением принтера на другой сервер, где он цепляется таким же скриптом usbip и расшаривается для основного сервера. Мда, костыль, ничего не скажешь, но тем не менее… Я к тому, что, возможно, следует все-же попытаться убивать процессы usbip утилитой handle, как и описано в статье.
по поводу скрипта в бубунте. В моем случае написан один скрипт, запускающийся на каждом LTSP-клиенте. В бесконечном цикле делаем команду листинга устройств, потом по mac-адресу сетевухи определяем, что за компьютер, на котором выполняемся. Далее в файле /tmp/usbip ищем id девайса, выдираем порт, на котором этот девайс висит (ведь его могут воткнуть в любой из портов 😉 ) и пробрасываем. Никаких отключений девайса я не делаю. Вот примерный текст скрипта:
где поиск вхождения строк usblp или usb-storage подразумевает то, что они на данный момент не проброшены.
Из проблем: некоторые флешки, после проброса наглухо вешают демон usbipd. Никакие перезапуски демона не помогают, только перезагрузка станции. Также при выдергивании флешки она больше не увидится демоном до перезапуска системы, чего не скажешь, например, про принтеры.
«Но данный подход обязывает устанавливать драйвера для принтера на клиентскую станцию.» — не знаю, откуда пошла эта теория, но с сожалением признаю, что я сам ее везде повторял. Так вот, это НЕ правда. Сейчас WTware научилась перенаправлять принтеры через RDP и логика там в точности такая же, как и у эмуляции принтсервера lp_server. Работают практически все принтеры кроме кэнонов. Никаких драйверов на клиенте мы для этого не ставили, принтеро-зависимая там только заливка прошивок в принтеры типа HP1020.
По теме: в дистрибутив wtware входит наша виндовая служба wtusbip. На стороне виндовса она заменяет usbip.exe и решает проблему1, просыпаясь и обращаясь к терминалу по команде самого терминала. Интерфейс управления ею с терминала простой, можно спросить у нас на форуме, можно посмотреть сниффером. С проблемой2 разбираемся, есть надежда решить ее красиво и опять же безо всяких вешних костылей. Наши серверные службы работают бесплатно 🙂
серьезно. Ничего не скажешь. Особенно учитывая, что Ваш продукт денег стоит. А если у меня подавляющее большинство этих злопринтеров?
а какой смысл, если она входит в дистрибутив, и соответственно, поставив его — компания попадает под лицензионную ответственность? Насколько я понял — отдельного дистрибутива нету.
а теперь смысл Вашего комментария (кроме саморекламы, конечно) ??
> А если у меня подавляющее большинство этих злопринтеров?
То вам придется использовать USBIP. Я только хотел отметить, что утверждение «данный подход обязывает устанавливать драйвера для принтера на клиентскую станцию» про проброс принтера через RDP — распространенное заблуждение.
> соответственно, поставив его – компания попадает под лицензионную ответственность?
Хм. Технических средств лицензирования у наших серверных служб нет, «купить лицензию» на них невозможно: лицензируются только терминалы. Но формально вы правы. Мне эта мысль в голову не приходила, даже когда я чинил наш tftp по жалобе «с него синстейшн не загружется».
> Насколько я понял – отдельного дистрибутива нету.
Где надо написать «использование наших серверных служб бесплатно, установка wtware на сервер с целью использования серверных служб разрешена без заключения договора и оплаты», чтобы люди по этому поводу не волновались?
> а теперь смысл Вашего комментария
В статье написано:
> Вот такой вот монструозный скрипт получился…
> Буду рад любым … более изящным решениям.
Скрипты же действительно монструозные. Решение «установить на сервер специально для этого сделанную службу и прикрутить к терминалу скрипт из пары строчек, обращающийся к ней» мне кажется более изящным.
По проблеме2 пришли положительные результаты от пользователей. Достаточно обучить определенное устройство на определенном терминале при каждом переподключении виснуть на один и тот же порт, и принтеры больше не размножаются. Это нельзя сделать через штатную usbip.exe, но драйвер usbip это умеет делать. Можно дописать и пересобрать usbip.exe, можно (реклама зачеркнута) 🙂
ну что ж, спасибо за идею. Действительно было бы изящнее, но реализовать это сложнее, нежели наклепать с десяток скриптов.
конечно, ибо порой нужно читать EULA даже к какому-нибудь драйверу. Таковы уж обязанности системного администратора — контроль за лицензионной чистотой.
Может кому пригодится. Есть сервер UNIX с вирт.машинами. Столкнулся с проблемой: при перезагрузке вирт.машины(клиента usbip) HASP ключи не подключаются повторно через usbip. Решение нужно корректно отключить их перед перезагрузкой или выключением, небольшим скриптом GPO. В виндовом оболочке команда в linux с сервером usbip: ПУТЬ_ДО_ФАЙЛА\plink.exe -ssh -pw ПАРОЛЬ_ОТ_КОМПА_С_USBIP_СЕРВЕРОМ root@ИП_СЕРВЕРА usbip_bind_driver —other ПОРТ_ДЛЯ_ОТКЛЮЧЕНИЯ
спасибо за совет. Но меня вот больше интересует, что делать с проброшенной с тонкого клиента флешкой, которая была вытащена без предупреждения из того же тонкого клиента. На клиенте USBIP (т.е. там, куда пробрасываем флешку) она больше не появится и заново «расшарить» на тонком клиенте эту флешку БЕЗ ПЕРЕЗАГРУЗКИ тонкого клиента уже не получится. Перезапуск usbip_bind_driver никак не помогает.
У меня получилось, в VBS создал цикл на 2 порта и отключал на горячую. Все нормально подключалось. Правда постоянно скрипт висит в оперативе, но нагрузки нету.
Пример:
do while i<1
wshell.Run "c:\usbip\usbip1\usbip.exe -a 192.168.0.1 1-1",0,true
wshell.Run "c:\usbip\usbip2\usbip.exe -a 192.168.0.1 1-2",0,true
loop
Спасибо за проделанный труд. С большим интересом почитал. Не все понятно.
Наверное что я сам весь слаб в VBS скриптах.
Есть вопрос такой.
Как выяснить, какой ID присвоен нужному нам принтеру на стороне сервера — понятно:
usbip -l 192.168.0.1
А вот как вывод этой команды (вернее именно номер BUSID) передать потом команде
usbip -a 192.168.0.1 — не понятно.
Я столкнулся с тем, что программа usbip.exe при ключе -l не выводит в stdout. Соответственно не удается завернуть этот вывод в файл.
usbip -l 192.168.0.1 > 1.txt приводит к наличию файла 1.txt нулевой длины.
Хотя usbip -h >help.txt отрабатывает нормально и файл help.txt наполняется.
Если есть какие мысли по этому поводу, подскажите
застрял на этом месте.
С уважением, Евгений.
Здравствуйте, Евгений. Дело в том, что список устройств (ихние BUSID), которые можно пробросить выдает демон usbip_bind_driver, запущенный с параметром -l на стороне сервера (машины, с которой пробрасываем устройство)
usbip_bind_driver -l
и выдает эта команда примерно такой результат:
List USB devices
— busid 1-4 (04a9:1904)
1-4:1.0 -> none
также существует команда сокращенного вывода списка устройств для последующей обработки выведенного текста скриптами:
usbip_bind_driver —list2
и вывод у данной команды будет таков:
busid=1-4#usbid=04a9:1904#1-4:1.0=none#
у себя я реализовал так: на сервере скрипт запускает данную команду и если находит в ней определенный идентификатор устройства (то что в данном примере 04a9:1904) то смотрит, на каком BUSID висит устройство (в данном примере 1-4), далее скрипт запускает уже саму команду проброса:
usbip_bind_driver —usbip 1-4
на стороне клиента же (windows-машинки, на которую пробрасываем устройство) запущенны скрипты, которые в бесконечном цикле пытаются подключить ВСЕ устройства 1-1, 1-2, 1-3, 1-4 и т.д . (командами usbip -a 192.168.0.1 1-x) с данной машинки. Таким образом, в какой порт не воткнул бы пользователь устройство — оно с одинаковым успехом пробросится на windows-машинку.
вот примерный вид скрипта, что крутится на сервере (машинке, с которой пробрасываем):
#!/bin/sh
#
while true; do
usbip_bind_driver —list2 > /tmp/usbip
if [ -n «$(cat /tmp/usbip | grep -o 04a9:1904)» ]; then
BUSID=`awk ‘-F#’ ‘$2==»usbid=04a9:1904″ {print $1}’ /tmp/usbip > /tmp/busid`
USBIP=`awk ‘-F=’ ‘$1==»busid» {print $2}’ /tmp/busid`
usbip_bind_driver —usbip $USBIP
exit
fi
sleep 5
done
вывод же списка устройств на windows машинке можно перенаправить в файл, но делать это нужно так:
usbip -l 192.168.0.1 2> 1.txt
обращаю внимание, что в данном случае вместо операнда «>» мы используем «2>»
Спасибо большое.
usbip -l 192.168.0.1 2> 1.txt очень помогло.
так же хочу отметить, у меня нет в сборке usb_bind_driver.
для просмотра — кто на usb я использую
# usbip list -l
Local USB devices
=================
— busid 4-1 (04e8:344f)
4-1:1.0 -> unknow
4-1:1.1 -> usbip-host
для запуска
modinfo usbip-host
modinfo usbip-core
usbip bind -b 4-1
возможно у нас разные версии…
ВНИМАНИЕ!! подозрение на то, что по ссылке вирус!! (прим. автора блога)
как все скорее всего знают, очень большой недостаток клиента usbip. он при обрыве связи виснет, и если у нас таких клиентов много, а вылетел только один, как определить что это нужный? фактически не реально … это переписанный .ехе . с возможностью вешать наши девайсы на определенный порт. к примеру
«> usbip-p.exe -а 10.10.1.1 2-2 1»
и наш принтер «повесится» на порт 1 и при обрыве связи, мы просто и безболезненно можем его «деатачить» «> usbip.exe -d 1» .
зы. в коде куча проверок .. и много закрытых и недоделанных функция . разбираться со всем желание нет .. по тому советую все команды использовать в родном .exe .А а модифицированным «атачить» на порт. Поясняю, если у вас есть «веселый велосипед» основанный на родном .exe то в этом он скорее всего работать перестанет.
Пользуемся 🙂 тестируем…
большое спасибо за доработку, но очень хотелось бы увидеть исходники утилиты. Простите, не в обиду Вам будет сказанно, но у меня взыгралась паранойя при наблюдении Вашей грамматики и отчета с вирустотала.
с этим могу поспорить, т.к. данный скрипт уже более как 1,5 года успешно работает в продакшене изо дня в день.
Исходники взяты с http://usbip.svn.sourceforge.net/ и заменены несколько переменных, по сути это тот же usbip.exe . С некоторыми поправками, по этому если у вас подозрение на вирь,советую проверить оригинальный, то же =будет :). Уверяю на 1000% .
А по поводу то что перестанет работать, там реализована куча проверок на правильность ввода и прочее. По этому некоторые функции отвалились , на пример «-l». По этому её я юзаю через оригинальный .exe . А само подключение непосредственно через модифицированный.
кому Вы рассказываете? вот отчет оригинального бинарника, прошу обратить внимание на дату последнего анализа. Так что исходники в студию. Иначе дело вообще пахнет керосином. Ваши уверения не стоят выеденного гроша, простите.
или Вы действительно настолько наивны, если полагаете, что люди, которые занимаются такими проектами, в которых нужны утилиты такого рода, действительно, вот так вот будут наобум запускать в продакшен непроверенные, непонятно откуда взятые, непонятно кем написанные бинарники? 🙂 смешно, да
Вы меня в конец запутали. Сначала Вы пишете:
теперь Вы, по всей видимости имеете в виду то, что велосипед ехать не будет, именно с Вашим бинарником, и поэтому его нужно использовать только для подключения устройства:
определитесь же, наконец. И да, повторюсь, не в обиду Вам, конечно, но профессия программиста не терпит такой вырвиглазной грамматики и пунктуации. Можете считать меня грамма-наци. Аминь.
P.S. Вы добились своей цели, вызвали во мне бурление говн. Трололо.
Ну в принципе не претендую на премию по грамматике .. всегда с русским беда была.
По поводу того, что нарушена логическая цепочка .. пока не показали, не заметил/
Да, не будет велосипед с модифицированным .exe работать.
И я слава богу не программист :). по этому тогда будем считать что мне простительны мои ошибки, и я не в коем случае не принуждаю людей использовать, я его для себя модифицировал. И просто выложил в сеть как есть, кому интересно может использовать.
Почему его вирус тотал определяет в базу , хз. Никаких глобальных изменений не проводилось.
если уж просто выложили бинарник, то и так же просто будет выложить исходники, я полагаю. Иначе и тему можно закрыть, как не несущую какой-либо ценности. Спасибо, за попытку помочь (возможно).