AShare UserName
Volume Number: | | 7
|
Issue Number: | | 6
|
Column Tag: | | Programmer's Forum
|
AppleShare Username to Chooser Name INIT
By Mark Gavin, Darby, PA
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
[ Mark Gavin is a consultant in the Philadelphia area who specializes in Macintosh software development using MacApp, Object Pascal and 68000 Assembly. He can be reached via AppleLink GAVIN.MARK. ]
INITs in General
It used to be that an INIT had to be loaded into the system file using ResEdit or some other resource installer. Only 32 INITs were allowed in the system file and you had to be aware of possible resource ID conflicts during the installation process. A while back Apple enhanced the INIT system by placing INIT 31 within the system file. The job of INIT 31 is to look in the system folder for any files of type INIT who happen to have one or more INIT resources. The search is in alphabetical order by filename. When INIT 31 locates an INIT resource it is called and executed as if it were a procedure (Inside Macintosh Volume IV, pp. 255-257) . INIT 31 can also allocate memory in the system heap for the INIT if you use the sysz resource (Inside Macintosh Volume V, pg. 352). As is, INIT 31 gives you at least a 16K block within the system heap.
An INIT in its basic form is a stand alone code module which normally has no access to global data or anything that uses A5 relative addressing like QuickDraw. This minor inconvenience can be easily circumvented by creating your own A5 world. There is a very good explanation, and examples, of setting up your own A5 world in Apples Macintosh Technical Note #256, Stand-Alone Code, ad nauseam.
Network Administration and Responder
One of my clients has implemented a wide area EtherNet network which covers 15 sites with 76 zones and 56 networks throughout the United States. Network support is via Timbuktu which uses the Chooser name to identify each Mac. They also use InterPoll and NetMinder for network diagnostics. These, also, use the Chooser name, broadcast by the Responder.
Responder is an INIT supplied by Apple on the Macintosh System software disks. Its function is to register the Macintosh Chooser name on the AppleShare network. The Responder and the other utilities described above, play an important role in network administration by allowing us to easily locate an individual Macintosh and its owner on the network. The Chooser name, however, can be easily modified or deleted by the user. For example, the network administrator, located in Pennsylvania, would have trouble determining who owns a Macintosh named Wizard located in California or Connecticut. In addition, there is no guarantee the Macintosh will have the same name tomorrow or the next day.
Placing the AppleShare username into the Chooser name appears to be the most straight forward solution to the problem of system identification and offers us several advantages:
1. Avoid burdening the user with yet one more administrative task.
2. Automatic update of Chooser name with new user.
3. The Chooser name can not be permanently modified by the user.
The AShareNameINIT
The function of the AShareNameINIT is to extract the AppleShare username from the AppleShare Prep file and place the username in the Chooser. I have chosen not to display an ICON at startup because this INIT is a network administration tool that should not be disabled by the user. Therefore, the less obtrusive the better.
The AppleShare Prep File
Apple hasnt released any information on the AppleShare Prep file, so this is what I have figured out thus far.
The AppleShare Prep file is created by selecting the AppleShare device within the Chooser. When the file is created it contains 4 bytes, all nil, within a single resource BMLS 2447. The AppleShare logon name is only placed into the BMLS resource if the user chooses to have an AppleShare volume auto-logon at startup. As a side note; If you elect to have AppleShare automatically logon to a server with your name and password, the password is also placed in the AppleShare Prep file but it is not encrypted in any way. By looking at the resource with ResEdit you can read any stored password.
The AppleShare Prep file saves a username and server history when the server name changes. There does not appear to be any way of deleting information that is no longer being used by AppleShare. This history is stored in a linked list of entries. The number of entries in the file is stored in the first word of the BMLS resource. The second word of the resource contains the size of the block to follow. It is also used as an offset to the next entry in the list. Logical block size within the resource is variable according to how many server disks are mounted. The username is a 32 byte Pascal string located 20 bytes after the blocksize. Currently, the INIT uses the last name in the list of AppleShare usernames located within the BMLS resource.
The Chooser Name
The Chooser Name is located in a STR resource within the System file whose ID is -16096. This is a Pascal string which is exactly 32 bytes. Any more or less may cause problems because all of the applications that use the Chooser name expect 32 bytes. Even-thought it is technically a STR resource, it would not be wise to try to fill it with a Str255.
The Code
The TYPE intPtr is used to coerce the Mac into returning a word instead of a byte as it would normally.
There is bug in the 128K and later versions of the ROM which will cause _OpenRFPerm not to pass trap discipline under TMON. When you hard code a string into the code resource, like I am doing with the AppleShare Prep filename, _OpenRFPerm calls _RecoverHandle with a bad Master Pointer. This is the reason for calling StripAddress on the address of the filename. For a more complete explanation please refer to Technical Note #232 Strip with _OpenResFile and _OpenRFPerm.
The ENTRYPOINT Procedure and the forward declaration of the AShareNameINIT procedure are needed because of the CloseAndExit nested procedure. If the forward declaration is not used the CloseAndExit procedure will be the first procedure executed within the INIT code segment and cause all kinds of nasty things to happen; i.e., trample on the stack.
The CloseAndExit procedure could have been avoided just by using a GOTO to jump to the CloseResFile statement at the end of the procedure. However, I think the nested procedure results in cleaner looking code even-though using a GOTO is a more efficient solution in this particular case.
I like to place the About information and the compilation date directly into the executable code. MPW Pascal, however, is an optimizing compiler which will strip out all string constants or code that are not referenced. The IF statement containing the deadStrip boolean is a quick and dirty way of bypassing the MPW Pascal compiler optimization and forcing an About string and the compilation date into the executable code segment. The deadStrip boolean is needed because simply setting the IF statement to false will cause the entire IF statement to be striped from the code.
When the user sets AppleShare to autologon to a server as a Guest, the AppleShare Prep file contains a null string. If this is the case, we do not want to copy a null string into the Chooser because Responder will ask the user to name the Macintosh after each restart. This would be rather annoying to the user.
Program Flow
1. Get the vRefNum of the startup volume.
2. Open the resource fork of the AppleShare Prep file.
3. Get a handle to BMLS 2447.
4. Get a handle to STR -16096.
5. Check for data in BMLS 2447.
6. How many entries are in the linked list.
7. Offset to the last entry in the linked list.
8. Check for a nil name.
9. Copy the name to STR -16096.
10. Cleanup.
Thats all there is to it. INITs, in general, are relatively straight forward to write. Just be careful and test using heap scramble, purge and trap discipline.
Bibliography
Macintosh Technical Notes:
#129 _SysEnvirons: System 6.0 and Beyond.
#232 Strip with _OpenResFile and _OpenRFPerm.
#247 Giving the (Desk)Hook to INITs.
#256 Stand-Alone Code, ad nauseam.
Listing: ASharINIT.p
{ --------------------------
AShareNameINIT
Type INIT
ID16056
Version1.5
Wednesday, December 5, 1990 9:53:37 AM
©1990 by Mark Gavin
All Rights Reserved
-------------------------- }
{$Z+} { Identify all routines to the Linker as
external. If this is not used then
ENTRYPOINT must be placed in the
INTERFACE section }
UNIT _AShareNameINIT;
INTERFACE
USES
Types, Memory, ToolUtils,
OSUtils, Resources, PaslibIntf, Packages;
TYPE
intPtr = ^integer;
{used to retrieve an integer from the resource}
{$R-} { Turn Off Range Checking }
{$OV-} { Ignore Arithmetic Overflows }
IMPLEMENTATION
PROCEDURE AShareNameINIT;FORWARD;
PROCEDURE ENTRYPOINT;
BEGIN
AShareNameINIT;
END;
{ ---------------------------------- }
PROCEDURE AShareNameINIT;
VAR
str : Str255;
volNamePtr : StringPtr;
theStringPtr,
theNamePtr : Ptr;
{ AppleShare Prep resHdl }
ASPResHandle,
chooserStringHdl: Handle;
ASPHandleSize : longint;
index,
blocksize,
resOffset,
offsetMultiplier,
stringLength,
refNum,
vRefNum,
oldResFileRef : integer;
anError: OSErr;
deadStrip: Boolean;
{ ------------------------------------ }
PROCEDURE CloseAndExit;
BEGIN
CloseResFile(refNum);
UseResFile(oldResFileRef);
EXIT(AShareNameINIT);
END;
{ ------------------------------------ }
BEGIN
oldResFileRef := CurResFile;
deadStrip := False;
IF deadStrip THEN BEGIN
str := AShareNameINIT 1.5 by Mark Gavin;
str := compdate;
END;
{ GetVol returns the name of the
default volume in volNamePtr and
its volume reference number in vRefNum. }
volNamePtr := StringPtr(NewPtr(256));
IF MemError <> noErr THEN
EXIT(AShareNameINIT);
anError := GetVol(volNamePtr, vRefNum);
IF anError <> noErr THEN
EXIT(AShareNameINIT);
DisposPtr(Ptr(volNamePtr));
{ Ptr only needed for GetVol }
str := AppleShare Prep;
{ StripAddress used per Tech Note 232
- bug in OpenRFPerm }
refNum := OpenRFPerm(StringPtr(StripAddress(@str))^, vRefNum, fsRdPerm);
IF ResError <> noErr THEN
EXIT(AShareNameINIT);
ASPResHandle := GetResource(BMLS, 2447);
IF ResError <> noErr THEN
CloseAndExit;
HNoPurge(ASPResHandle);
chooserStringHdl := GetResource(STR , -16096);
IF ResError <> noErr THEN
CloseAndExit;
HNoPurge(chooserStringHdl);
ASPHandleSize := GetHandleSize(ASPResHandle);
IF MemError <> noErr THEN
CloseAndExit;
{ Make sure there is data in the handle }
IF ASPHandleSize < 55 THEN
CloseAndExit;
offsetMultiplier := intPtr(ASPResHandle^)^;
IF offsetMultiplier < 1 THEN
CloseAndExit;
blockSize := intPtr(ORD4(ASPResHandle^) + 2)^;
IF blockSize < 1 THEN
CloseAndExit;
resOffset := 2;
FOR index := 1 TO (offsetMultiplier - 1) DO
BEGIN
resOffset := resOffset + blockSize;
blockSize := intPtr(ORD4(ASPResHandle^) + resOffset)^;
IF blockSize < 1 THEN
CloseAndExit;
END;
resOffset := resOffset + 20;
{ offset to string within block }
theNamePtr := Ptr(ORD4(ASPResHandle^) + resOffset);
IF theNamePtr^ = 0 THEN
CloseAndExit;{ exit on nil name }
theStringPtr := chooserStringHdl^;
BlockMove(theNamePtr, theStringPtr, 32);
ChangedResource(chooserStringHdl);
WriteResource(chooserStringHdl);
HPurge(chooserStringHdl);
HPurge(ASPResHandle);
CloseResFile(refNum);
UseResFile(oldResFileRef);
END;
END.
Listing: AShareINIT.make
{ -------------------------- }
# File: AShareNameINIT.make
# Target: AShareNameINIT
# Sources: AShareNameINIT.p
# Created: Monday, November 26, 1990 4:39:50 PM
#©1990 by Mark Gavin
AShareNameINIT.p.o AShareNameINIT.make AShareNameINIT.p
Pascal AShareNameINIT.p
SOURCES = AShareNameINIT.p
OBJECTS = AShareNameINIT.p.o
AShareNameINIT AShareNameINIT.make AShareNameINIT.p.o
Link -t INIT -c ASN? -rt INIT=16056 -ra =resLocked -sg AShareNameINIT
-m ENTRYPOINT
{OBJECTS}
{Libraries}Interface.o
-o AShareNameINIT