1 /*
2  * ScsiDeviceList.cpp - Class which provides information on installed devices.
3  *
4  * Author: Robert Nelson, August, 2006 <robertn@the-nelsons.org>
5  *
6  * Version $Id$
7  *
8  * This file was contributed to the Bacula project by Robert Nelson.
9  *
10  * Robert Nelson has been granted a perpetual, worldwide,
11  * non-exclusive, no-charge, royalty-free, irrevocable copyright
12  * license to reproduce, prepare derivative works of, publicly
13  * display, publicly perform, sublicense, and distribute the original
14  * work contributed by Robert Nelson to the Bacula project in source
15  * or object form.
16  *
17  * If you wish to license contributions from Robert Nelson
18  * under an alternate open source license please contact
19  * Robert Nelson <robertn@the-nelsons.org>.
20  */
21 /*
22    Bacula® - The Network Backup Solution
23 
24    Copyright (C) 2006-2006 Free Software Foundation Europe e.V.
25 
26    The main author of Bacula is Kern Sibbald, with contributions from
27    many others, a complete list can be found in the file AUTHORS.
28    This program is Free Software; you can redistribute it and/or
29    modify it under the terms of version three of the GNU Affero General Public
30    License as published by the Free Software Foundation and included
31    in the file LICENSE.
32 
33    This program is distributed in the hope that it will be useful, but
34    WITHOUT ANY WARRANTY; without even the implied warranty of
35    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36    General Public License for more details.
37 
38    You should have received a copy of the GNU Affero General Public License
39    along with this program; if not, write to the Free Software
40    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
41    02110-1301, USA.
42 
43    Bacula® is a registered trademark of Kern Sibbald.
44    The licensor of Bacula is the Free Software Foundation Europe
45    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
46    Switzerland, email:ftf@fsfeurope.org.
47 */
48 
49 #if defined(_MSC_VER) && defined(_DEBUG)
50 #include <afx.h>
51 #else
52 #include <windows.h>
53 #endif
54 
55 #include <stdio.h>
56 #include <tchar.h>
57 
58 #include "ScsiDeviceList.h"
59 
60 #if defined(_MSC_VER) && defined(_DEBUG)
61 #define  new   DEBUG_NEW
62 #endif
63 
64 TCHAR    CScsiDeviceList::c_ScsiPath[] = _T("HARDWARE\\DEVICEMAP\\Scsi");
65 
66 LPCTSTR  CScsiDeviceList::c_lpszFormatList[] =
67 {
68    _T("Logical Unit Id %d"),
69    _T("Target Id %d"),
70    _T("Scsi Bus %d"),
71    _T("Scsi Port %d")
72 };
73 
74 LPCTSTR  CScsiDeviceListEntry::c_DeviceTypes[] =
75 {
76    _T("Unknown"),
77    _T("CDRom"),
78    _T("Changer"),
79    _T("Disk"),
80    _T("Tape")
81 };
82 
CScsiDeviceListEntry(const CScsiDeviceListEntry & other)83 CScsiDeviceListEntry::CScsiDeviceListEntry(const CScsiDeviceListEntry &other)
84 {
85    m_eDeviceType = other.m_eDeviceType;
86 
87    m_lpszIdentifier = other.m_lpszIdentifier != NULL ? _tcsdup(other.m_lpszIdentifier) : NULL;
88 
89    m_lpszDeviceName = other.m_lpszDeviceName != NULL ? _tcsdup(other.m_lpszDeviceName) : NULL;
90 
91    m_dwDeviceId = other.m_dwDeviceId;
92    _tcscpy(m_szDevicePath, other.m_szDevicePath);
93 }
94 
CScsiDeviceListEntry(void)95 CScsiDeviceListEntry::CScsiDeviceListEntry(void)
96 {
97    m_eDeviceType = Unknown;
98    m_lpszIdentifier = NULL;
99    m_lpszDeviceName = NULL;
100    m_dwDeviceId = 0;
101    m_szDevicePath[0] = _T('\0');
102 }
103 
~CScsiDeviceListEntry(void)104 CScsiDeviceListEntry::~CScsiDeviceListEntry(void)
105 {
106    if (m_lpszIdentifier != NULL)
107    {
108       free(m_lpszIdentifier);
109    }
110 
111    if (m_lpszDeviceName != NULL)
112    {
113       free(m_lpszDeviceName);
114    }
115 }
116 
117 bool
Populate()118 CScsiDeviceList::Populate()
119 {
120    this->clear();
121 
122    HKEY  hScsiKey;
123 
124    _tcscpy(m_szLastKey, _T("\\Scsi"));
125    m_dwLastKeyLength = 5;
126 
127    m_lLastError = RegOpenKeyEx(  HKEY_LOCAL_MACHINE,
128                                  c_ScsiPath,
129                                  0,
130                                  KEY_READ,
131                                  &hScsiKey);
132 
133    if (m_lLastError != ERROR_SUCCESS) {
134       _tcscpy(m_szLastOperation, _T("Opening key "));
135       _tcscpy(m_szLastKey, c_ScsiPath);
136       return false;
137    }
138 
139    if (!ProcessKey(hScsiKey, c_MaxKeyDepth - 1, 0)) {
140       return false;
141    }
142 
143 #if defined(_DEBUG)
144    _fputtc(_T('\n'), stderr);
145 #endif
146 
147    return true;
148 }
149 
150 bool
ProcessKey(HKEY hKey,int iLevel,DWORD dwDeviceId)151 CScsiDeviceList::ProcessKey(HKEY hKey, int iLevel, DWORD dwDeviceId)
152 {
153 #if defined(_DEBUG)
154    switch (iLevel)
155    {
156    case 3:
157       _ftprintf(  stderr,
158                   _T("%-64s\n"),
159                   &m_szLastKey[1]);
160       break;
161 
162    case 2:
163       _ftprintf(  stderr,
164                   _T("%-64s%d\n"),
165                   &m_szLastKey[1],
166                   dwDeviceId & 0xFF);
167       break;
168 
169    case 1:
170       _ftprintf(  stderr,
171                   _T("%-64s%d:%d\n"),
172                   &m_szLastKey[1],
173                   (dwDeviceId >>  8) & 0xFF,
174                    dwDeviceId        & 0xFF);
175       break;
176 
177    case 0:
178       _ftprintf(  stderr,
179                   _T("%-64s%d:%d:%d\n"),
180                   &m_szLastKey[1],
181                   (dwDeviceId >>  16) & 0xFF,
182                   (dwDeviceId >>  8) & 0xFF,
183                    dwDeviceId        & 0xFF);
184       break;
185    }
186 #endif
187 
188    for (int idxSubkey = 0; ; idxSubkey++) {
189 
190       TCHAR szSubkeyName[c_MaxSubkeyLength + 1];
191       DWORD dwLength;
192 
193       dwLength = sizeof(szSubkeyName);
194 
195       m_lLastError = RegEnumKeyEx(  hKey,
196                                     idxSubkey,
197                                     szSubkeyName,
198                                     &dwLength,
199                                     NULL,
200                                     NULL,
201                                     NULL,
202                                     NULL);
203 
204       if (m_lLastError == ERROR_NO_MORE_ITEMS) {
205          break;
206       } else  if (m_lLastError == ERROR_MORE_DATA) {
207 #if defined(_DEBUG)
208          _tcscpy(m_szLastOperation, _T("Enumerating subkeys of "));
209          PrintLastError();
210 #endif
211          // Subkey name is too long
212          continue;
213       } else if (m_lLastError != ERROR_SUCCESS) {
214          // Unexpected Error
215          _tcscpy(m_szLastOperation, _T("Enumerating subkeys of "));
216          return false;
217       }
218 
219       int   iValue;
220 
221       if (_stscanf(szSubkeyName, c_lpszFormatList[iLevel], &iValue) != 1) {
222          // Ignore this subkey, it is probably Initiator Id n
223          continue;
224       }
225 
226       m_szLastKey[m_dwLastKeyLength++] = _T('\\');
227 
228       DWORD dwSubkeyLength = (DWORD)_tcslen(szSubkeyName);
229       memcpy(&m_szLastKey[m_dwLastKeyLength], szSubkeyName, (dwSubkeyLength + 1) * sizeof(TCHAR));
230       m_dwLastKeyLength += dwSubkeyLength;
231 
232       HKEY  hSubkey;
233 
234       m_lLastError = RegOpenKeyEx(hKey, szSubkeyName, 0, KEY_READ, &hSubkey);
235 
236       if (m_lLastError != ERROR_SUCCESS) {
237          _tcscpy(m_szLastOperation, _T("Opening key "));
238          return false;
239       }
240 
241       if (iLevel == 0) {
242 #if defined(_DEBUG)
243          _ftprintf(  stderr,
244                      _T("%-64s%d:%d:%d:%d\n"),
245                      &m_szLastKey[1],
246                      (dwDeviceId >> 16) & 0xFF,
247                      (dwDeviceId >>  8) & 0xFF,
248                       dwDeviceId        & 0xFF,
249                       iValue);
250 #endif
251 
252          ProcessValues(hSubkey, (dwDeviceId << 8) | iValue);
253       } else {
254          if (!ProcessKey(hSubkey, iLevel - 1, (dwDeviceId << 8) | iValue)) {
255             return false;
256          }
257       }
258 
259       m_dwLastKeyLength -= dwSubkeyLength;
260       m_dwLastKeyLength--;
261       m_szLastKey[m_dwLastKeyLength] = _T('\0');
262    }
263 
264    return true;
265 }
266 
267 bool
ProcessValues(HKEY hKey,DWORD dwDeviceId)268 CScsiDeviceList::ProcessValues(HKEY hKey, DWORD dwDeviceId)
269 {
270    CScsiDeviceListEntry    EntryTemplate;
271    DWORD                   dwType;
272    DWORD                   dwSize;
273    TCHAR                   szValue[c_MaxValueLength + 1];
274 
275    this->push_back(EntryTemplate);
276    CScsiDeviceListEntry &  entry = this->back();
277 
278    dwSize = sizeof(szValue);
279 
280    m_lLastError = RegQueryValueEx(  hKey,
281                                     _T("Identifier"),
282                                     NULL,
283                                     &dwType,
284                                     (LPBYTE)&szValue[0],
285                                     &dwSize);
286 
287    if (m_lLastError == ERROR_SUCCESS) {
288       entry.m_lpszIdentifier = _tcsdup(szValue);
289    } else {
290 #if defined(_DEBUG)
291       _tcscpy(m_szLastOperation, _T("Reading value "));
292       PrintLastError(_T("Identifier"));
293 #endif
294    }
295 
296    dwSize = sizeof(szValue);
297 
298    m_lLastError = RegQueryValueEx(  hKey,
299                                     _T("DeviceName"),
300                                     NULL,
301                                     &dwType,
302                                     (LPBYTE)&szValue[0],
303                                     &dwSize);
304 
305    if (m_lLastError == ERROR_SUCCESS) {
306       entry.m_lpszDeviceName = _tcsdup(szValue);
307    } else {
308 #if defined(_DEBUG)
309       _tcscpy(m_szLastOperation, _T("Reading value "));
310       PrintLastError(_T("DeviceName"));
311 #endif
312    }
313 
314    dwSize = sizeof(szValue);
315 
316    m_lLastError = RegQueryValueEx(  hKey,
317                                     _T("Type"),
318                                     NULL,
319                                     &dwType,
320                                     (LPBYTE)&szValue[0],
321                                     &dwSize);
322 
323    if (m_lLastError == ERROR_SUCCESS) {
324       if (_tcscmp(_T("CdRomPeripheral"), szValue) == 0) {
325          entry.m_eDeviceType = CScsiDeviceListEntry::CDRom;
326       } else if (_tcscmp(_T("DiskPeripheral"), szValue) == 0) {
327          entry.m_eDeviceType = CScsiDeviceListEntry::Disk;
328       } else if (_tcscmp(_T("MediumChangerPeripheral"), szValue) == 0) {
329          entry.m_eDeviceType = CScsiDeviceListEntry::Changer;
330       } else if (_tcscmp(_T("TapePeripheral"), szValue) == 0) {
331          entry.m_eDeviceType = CScsiDeviceListEntry::Tape;
332       }
333    } else {
334 #if defined(_DEBUG)
335       _tcscpy(m_szLastOperation, _T("Reading value "));
336       PrintLastError(_T("Type"));
337 #endif
338    }
339 
340    entry.m_dwDeviceId = dwDeviceId;
341 
342    return true;
343 }
344 
345 void
PrintLastError(LPTSTR lpszName)346 CScsiDeviceList::PrintLastError(LPTSTR lpszName)
347 {
348    LPTSTR   lpszMessage = NULL;
349 
350    _fputts(_T("Error: "), stderr);
351    _fputts(m_szLastOperation, stderr);
352    _fputtc(_T('"'), stderr);
353    _fputts(lpszName != NULL ? lpszName : m_szLastKey, stderr);
354    _fputts(_T("\" - "), stderr);
355 
356    FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
357                   NULL, m_lLastError, 0, (LPTSTR)&lpszMessage, 0, NULL);
358 
359    if (lpszMessage != NULL) {
360       _fputts(lpszMessage, stderr);
361       LocalFree(lpszMessage);
362    }
363 }
364