![]() |
Using SYSLEVEL Files in Your ApplicationsWritten by Martin Lafaix |
IntroductionWhat's that?Determining a computer software configuration has always been a tricky process. It would be great to have a standard way for registering applications, so that we could easily query the system configuration. But wait a minute! OS/2 provides the SYSLEVEL command, which does just that (it even tells us the various component versions)! If only we could use it for our own purposes...Why?This registering ability proves its usefulness when writing (relatively) large projects or components. For example, if you write a public toolkit or tools other programs may use, it would be a good idea to register them. (OS/2 already uses it this way: on my home system, the syslevel command tells me that MMPM/2 and the Developer's toolkit are present - the same thing occurs if you have Communication Manager, etc.) When distributing such a tool, tell your users your component name and ID; it would then be easy for them to write an installation procedure which checks your component presence.Another interest is that it helps component writers in creating CSD or component updates. As each SYSLEVEL file maintains a current CSD level as well as a previous CSD level field, it's easy to check what to update, and what to leave alone. It's also useful while maintaining a computer network. It would provide the maintainer an up-to-date information database. ContentsThis article contains two parts. The first one describes the SYSLEVEL file format. The second part describes the two proposed APIs (one for C, and one for Rexx).
A Voyage to SyslevelWhen you start SYSLEVEL.EXE, it scans all your drives for SYSLEVEL files. These files are named SYSLEVEL.xxx (where xxx is any three-letters extension), and have a specific header. When one such file is found, it is displayed, as shown in figure 1. So, to be able to use SYSLEVEL for our own purpose, we'll have to decrypt SYSLEVELs' file headers, and find out how (and where) to store our own data.C:\MMOS2\INSTALL\SYSLEVEL.MPM Multimedia Presentation Manager 2 Extensions systŠme 1.10 ID de composant 562137400 Type MMPM\2 Niveau de modifications en cours : UN00000 Niveau de modifications ant‚rieur : UN00000Figure 1. Syslevel output sample (French version, sorry :-) ) On the File HeaderA SYSLEVEL file header looks like the following:typedef struct _SYSLEVELHEADER { unsigned char h_magic[2]; unsigned char h_name[9]; unsigned char h_reserved1[4]; unsigned char h_updated; unsigned char h_reserved2[17]; ULONG h_data; } SYSLEVELHEADER;Figure 2. The SYSLEVELHEADER structure.
While I realize there's still many holes in there, it appears to fulfill our needs; namely, we are now able to create files recognized by SYSLEVEL.EXE. Isn't that great? But it's time to learn more... The SYSLEVEL Data StructureIn the previous section, we have seen that the file's data structure starts at offset h_data. This structure (aka. the SYSLEVEL data structure) looks like the following:typedef struct _SYSLEVELDATA { unsigned char d_reserved1[2]; unsigned char d_kind; unsigned char d_version[2]; unsigned char d_reserved2[2]; unsigned char d_clevel[7]; unsigned char d_reserved3; unsigned char d_plevel[7]; unsigned char d_reserved4; unsigned char d_title[80]; unsigned char d_cid[9]; unsigned char d_revision; unsigned char d_type[1]; } SYSLEVELDATA;Figure 3. The SYSLEVELDATA structure.
Home, Sweet HomeThis walk was fun, but all good things must come to an end. So we are back. And it's time to write some functions to comfort our precious new knowledge, isn't it?Two APIs are available, a C one and a REXX one. The SYSLEVEL C APIThis section describes the API which provides access to the syslevel files. The corresponding code is in CLEVEL.ZIP. It's just an include file, level.h and a small C file, level.c which can be included, say, in your favorite library. It has been tested with GCC/2, but it should work with any other C compiler...APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ pszCID) APIRET LvlCloseLevelFile(HFILE hFile) APIRET LvlQueryLevelFile (PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG ulBufSize) APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize) APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize) Design considerationsThis API has been modeled upon the profile (Prf) functions.ExamplesThe following two figures show how to check for the existence of a component, and if a component needs updating, respectively.: #include <level.h> : int main() { HFILE hFile; : if(hFile = LvlOpenLevelFile("DBM", OLF_SCANDISKS | OLF_CHECKID, "123456789")) /* present */ : else /* not present */ : : LvlCloseLevelFile(hFile); : }Figure 4. Syslevel API usage sample - Checking for the existence of a component : #include <level.h> : BOOL _needUpdate(HFILE hFile) { CHAR achCSD[7]; ULONG ulCount; if(LvlQueryLevelData(hFile, QLD_CURRENTCSD, achCSD, 7, &ulCount)) /* shall we update it? */ return strncmp(achCSD, "DBF0099",7) <= 0; /* for example */ else /* an error occurred */ : } :Figure 5. Syslevel API usage sample - Shall we update component? The SYSLEVEL REXX APIWell, it's not really an API, it's in fact a single function. The corresponding code is in RLEVEL.ZIP. It's just a REXX file, named REXXLVL.CMD. Put it somewhere along your PATH. The zip file also contains MAKESYSL.CMD, which can be used to create SYSLEVEL files.This function has been modeled upon the STREAM function. RexxLvl() ExampleThe following example is a REXX script. It allows you to easily create syslevel files./* makesysl.cmd 940327 */ say 'Component name:'; parse pull name say 'Component ID:'; parse pull ID say 'Component type (optional):'; parse pull type say 'Component version (as x.y[.z]):'; parse pull version say 'Current CSD Level:'; parse pull CSD say 'Component extension:'; parse pull ext file = RexxLvl("n", ext, ID) call RexxLvl "s", file, "n", name call RexxLvl "s", file, "c", csd call RexxLvl "s", file, "v", version call RexxLvl "s", file, "t", type rc = RexxLvl("c", file) if rc = 0 then say 'SYSLEVEL.'ext' created.'Figure 6. Creating a syslevel file in REXX. SummaryNow you have enough information to use the SYSLEVEL files well. However, the advertised advantages would become a reality only if we use SYSLEVEL files extensively. So, if you like these perspectives, use them, ask them, require them, again and again, until you get satisfaction. :-)As a sidenote, the information exposed in the first part of this article is the result of my own exploration. If you find any inaccuracy or if you have more information, please, let me know.
kind
ulOpenMode - input
ulWhat - inputThe following flags can be used when querying (or writing) a SYSLEVEL file.
LvlOpenLevelFileAPIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ pszCID)Use this to find, open or create a SYSLEVEL file. It returns the following values: 0 NO_ERROR 2 ERROR_FILE_NOT_FOUND 3 ERROR_PATH_NOT_FOUND 4 ERROR_TOO_MANY_OPEN_FILES 5 ERROR_ACCESS_DENIED 12 ERROR_INVALID_ACCESS 26 ERROR_NOT_DOS_DISK 32 ERROR_SHARING_VIOLATION 36 ERROR_SHARING_BUFFER_EXCEEDED 82 ERROR_CANNOT_MAKE 87 ERROR_INVALID_PARAMETER 99 ERROR_DEVICE_IN_USE 108 ERROR_DRIVE_LOCKED 110 ERROR_OPEN_FAILED 112 ERROR_DISK_FULL 206 ERROR_FILENAME_EXCEED_RANGE 231 ERROR_PIPE_BUSY pszName - inputThe syslevel file extension. It's a three-letter, null-terminated string. That is, LvlOpenLevelFile deals with syslevel files named syslevel.xxx, where xxx is pszName's value.
LvlCloseLevelFile APIRET LvlCloseLevelFile(HFILE hFile)Use this to close a SYSLEVEL file. It's just a DosClose alias, for API consistence. It returns the following values: 0 NO_ERROR 2 ERROR_FILE_NOT_FOUND 5 ERROR_ACCESS_DENIED 6 ERROR_INVALID_HANDLE LvlQueryLevelDataAPIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)Use this to query an already opened SYSLEVEL file. It returns the following values: 0 NO_ERROR 6 ERROR_INVALID_HANDLE 87 ERROR_INVALID_PARAMETER 122 ERROR_INSUFFICIENT_BUFFERWhen it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum requested size.
pBuffer - input/outputAddress of the buffer that contains the data to write/read.
pulSize - outputAddress of the variable to receive the number of bytes actually read or written.
LvlWriteLevelDataAPIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)Use this to update an already opened SYSLEVEL file. It returns the following values: 0 NO_ERROR 6 ERROR_INVALID_HANDLE 19 ERROR_WRITE_PROTECT 29 ERROR_WRITE_FAULT 87 ERROR_INVALID_PARAMETER 122 ERROR_INSUFFICIENT_BUFFERWhen it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum requested size.
ulBufSize - inputThe length, in bytes, of pBuffer. This is the number of bytes to be read or write.
phFile - outputAddress of the handle for the file.
pszCID - inputIt's the component ID string. It's required when you specify the OLF_CHECKID flag when opening a syslevel flag. It's a nine-digit string, not null-terminated, which uniquely identifies the component in conjunction with pszName. That is, you can have multiple SYSLEVEL files with the same name, but you can't have multiple syslevel files with the same name and ID.If you are not using the OLF_CHECKID flag, set pszCID to NULL.
RexxLvl()![]() RexxLvl returns a string describing the state of, or the result of an operation upon, a SYSLEVEL file. This function is used to request information on the state of a SYSLEVEL file, or to carry out some specific operation on the SYSLEVEL file. The first argument can be one of the following strings (of which only the first letter is needed) which describes the action to be carried out:
LvlQueryLevelFileAPIRET LvlQueryLevelFile(PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG ulBufSize)Use this to query/find existing SYSLEVEL files. This function enumerates all the SYSLEVEL files present in the system and returns the names as a list in the pBuffer parameter. Each SYSLEVEL file name is terminated with a NULL character and the last name is terminated with two successive NULL characters. Specifying pszName or pszCID (or both) restricts the enumeration to the corresponding SYSLEVEL files. It returns the following values: 0 NO_ERROR 87 ERROR_INVALID_PARAMETER 122 ERROR_INSUFFICIENT_BUFFERWhen it returns ERROR_INSUFFICIENT_BUFFER, the first ulBufSize bytes of pBuffer are correctly filled. |