/* * CONSVAR -- read and write SRM console environment variables * * THIS PROGRAM IS UNSUPPORTED UTILITY. USE AT YOUR OWN RISK! * * THIS PROGRAM HAS BEEN KNOWN TO CRASH AT LEAST ONE DUAL CPU DS25 * SO NO GUARANTEES ARE GIVEN FOR THIS PROGRAM TO FUNCTION CORRECTLY. * * You must have CMKRNL priviledge to run this program. It is * recommended that only one copy of this program runs at any one time. * * Done by Teijo Forsell on the basis of DEFAULT_BOOT.C./18.1.2007 * (also fixed few bugs in the original program like * the for loops had wrong expression order and * some code was accidentally duplicated) * * Usage scenario example: * You can successfully boot a system from a different boot disk remotely * by changing the boot_dev (note: not the bootdef_dev!) to point to * the new disk. * * Build and use with: * * $ cc consvar+sys$library:sys$lib_c.tlb/lib/warn=noinform * $ link consvar/sysexe * $ consvar:==$sys$disk:[]consvar * $ consvar boot_dev dka400 !Change "restart boot" to happen * !from dka400 * $ consvar bootdef_dev dka0,dka100 !Change "power on" or manual boot to * !happen from dka0 or dka100 in that * !order which ever is available. */ #pragma module consvar #include #include #include #include #include #include /* Local definitions */ #define BUF$K_LENGTH 132 /* External global variables */ extern HWRPB *exe$gpq_hwrpb; /* External routines */ extern int sys$cmkrnl (); extern void exit (); extern int strlen (); /* Routines defined in this module */ uint64 get_env (); uint64 set_env (); /* * Global array to associate environment variable numbers with * strings. Though it does not say so in the SRM, some of the * environment variables can be returned in different forms. For * example, if you say ...get_env ("bootdef_dev") you get the * name of the boot device in the form dka0.0.2.6.0. But if * you say ...get_env (3) you get the full path of the boot * device SCSI 0 0 2 6 0 -- very different. So, here I am * making a table which can be indexed by environment variable * number. The entries point to strings corresponding to the * environment variable number. See page 2-27 in the console * chapter of the SRM. */ static char *env_strings [] = { "reserved", "auto_action", "boot_dev", "bootdef_dev", "booted_dev", "boot_file", "booted_file", "boot_osflags", "booted_osflags", "boot_reset", "dump_dev", "enable_audit", "license", "char_set", "language", "tty_dev" }; main (argc, argv) int argc; char *argv []; { int status; int i; int num_bytes; int given_var_number = 0; int arglist [6]; char cons_var_buf [BUF$K_LENGTH]; char given_var [BUF$K_LENGTH]; char new_value [BUF$K_LENGTH]; uint64 callback_status; /* ALWAYS print this WARNING */ printf("THIS PROGRAM IS UNSUPPORTED UTILITY. USE AT YOUR OWN RISK!\n"); /* Print usage info if parameters missing or too much */ if(argc == 1 || argc > 3) { printf("Usage: consvar []\n"); printf("More help and variable number & name list with: consvar help\n"); exit (0); } /* Print list of variable names and their respective numbers if "help" given */ if(argc==2) { if (!strcmp(argv[1],"help")) { printf("Usage: consvar []\n"); printf("Variable can be given either in numerical form or string form.\n"); printf("Note that some variables are readonly in reality (like booted_dev for example).\n"); printf("This program will still try to write to them ;-) \n\n"); for(i=0;i<16;i++) { if (!strcmp(env_strings[i],"bootdef_dev")) { printf("%i %s (notice & try the odd difference between 3 and bootdef_dev)\n",i,env_strings[i]); printf(" (it is due to the SRM routines, but I don't know why)\n"); } else printf("%i %s\n",i,env_strings[i]); } exit (0); } } /* Initialize character buffers with zeros */ for (i=0 ; i < BUF$K_LENGTH ; i++) { cons_var_buf [i] = (char) 0; given_var [i] = (char) 0; new_value [i] = (char) 0; } /* argc must now be either 2 or 3 so copy the first parameter at least */ strcpy(given_var, argv[1]); /* if user gave variable in number format, then put that value in correct var */ if (atoi(given_var)>0) given_var_number=atoi(given_var); /* if arc is 3 then user gave us also the new value to deposit */ if(argc == 3) { strcpy(new_value, argv[2]); } /* * * * First time READ call * * */ /* Call kernel routine to read environment variable cons_var_buf */ arglist [0] = 4; arglist [1] = given_var_number; arglist [2] = (int) given_var; arglist [3] = (int) cons_var_buf; arglist [4] = (int) &callback_status; status = sys$cmkrnl (get_env, arglist); if (!(status & 1)) exit (status); /* * Console callbacks return status in bits 63:61. For get_env, * if the status is success, the number of bytes returned is * in the lower longword of the status quadword. So, break the * status into two longwords and look it over. */ status = callback_status >> 61; num_bytes = callback_status; /* * The callback status is as follows (now in bits 2:0) * 000 = success * 001 = success, byte stream truncated * 110 = environment variable not recognized */ switch (status) { case 0: /* Success */ if (argc==3) printf ("Old value: %s\n", cons_var_buf); else printf ("%s\n", cons_var_buf); break; case 1: { /* Success, but byte stream truncated. Display * what we got and exit. */ printf ("CONS_VAR_BUF partially read. %d bytes read\n", num_bytes); printf ("CONS_VAR_BUF : %s\n", cons_var_buf); printf ("Exiting...\n"); exit (0); } case 6: { printf ("Environment variable not recognized. Exiting...\n"); exit (0); } default: { printf ("Unknown status returned by get_env: %d\n", status); printf ("Exiting...\n"); exit (0); } } /* end switch (status) */ /* * * * First time READ call - end * * */ /* If the user also gave the new value to deposit, then we will do that and then also read the new value again */ if (argc==3) { /* If the variable name was given as name (and not number) then we have to change it to number during the deposit (it only works with numbers I think */ if (atoi(given_var)==0) { for (i=0 ; i<16 ; i++) if (!strcmp(env_strings[i],given_var)) { given_var_number=i; break; } } /* * * WRITE THE VALUE * */ printf("Given new value: %s\n",new_value); arglist [0] = 3; arglist [1] = given_var_number; /* EV to be written */ arglist [2] = (int) new_value; /* String to be written to EV */ arglist [3] = (int) &callback_status; status = sys$cmkrnl (set_env, arglist); if (!(status & 1)) exit (status); /* * Console callbacks return status in bits 63:61. For set_env, * if the status is success, everything is written. * Break the status into two longwords and look it over. */ status = callback_status >> 61; num_bytes = callback_status; /* * The set_env callback status is as follows (in bits 2:0) * 000 = success * 100 = fail, variable is read only * 110 = environment variable not recognized * 111 = fail, byte stream exceeds value length */ if (status != 0) { printf ("SET_ENV failed. Callback status : %d\n", status); printf ("Exiting...\n"); exit (0); } /* * * WRITE THE VALUE - END * */ /* * * * Last time READ call * * */ /* Initialize character buffer with zeros */ for (i=0 ; i < BUF$K_LENGTH ; i++) { cons_var_buf [i] = (char) 0; } /* Reset given_var_number to 0 if user gave the variable name as a string. This keeps outputs consistent for him at least with the bootdef_dev variable */ if (atoi(given_var)==0) given_var_number=0; /* Call kernel routine to read environment variable cons_var_buf */ arglist [0] = 4; arglist [1] = given_var_number; arglist [2] = (int) given_var; arglist [3] = (int) cons_var_buf; arglist [4] = (int) &callback_status; status = sys$cmkrnl (get_env, arglist); if (!(status & 1)) exit (status); /* * Console callbacks return status in bits 63:61. For get_env, * if the status is success, the number of bytes returned is * in the lower longword of the status quadword. So, break the * status into two longwords and look it over. */ status = callback_status >> 61; num_bytes = callback_status; /* * The callback status is as follows (now in bits 2:0) * 000 = success * 001 = success, byte stream truncated * 110 = environment variable not recognized */ switch (status) { case 0: /* Success */ if (argc==3) printf ("New value: %s\n", cons_var_buf); else printf ("%s\n", cons_var_buf); exit (0); case 1: { /* Success, but byte stream truncated. Display * what we got and exit. */ printf ("CONS_VAR_BUF partially read. %d bytes read\n", num_bytes); printf ("CONS_VAR_BUF : %s\n", cons_var_buf); printf ("SOMETHING WENT TERRIBLY WRONG!!! INIT THIS VAR FROM CONSOLE MANUALLY!!\n"); exit (0); } case 6: { printf ("Environment variable not recognized. Exiting...\n"); exit (0); } default: { printf ("Unknown status returned by get_env: %d\n", status); printf ("Exiting...\n"); exit (0); } } /* end switch (status) */ /* * * * Last time READ call - end * * */ } /* Final exit of "main" */ exit (SS$_NORMAL); } /* * * Subroutines to read and set the variables in SRM * */ uint64 get_env (int env_number, char *env_string, char *buffer, uint64 *callback_status) /* * Routine get_env -- performs get_env console callback to read * specified environment variable * * INPUTS * env_number Number of environment variable. * See SRM or HWRPBDEF.H * buffer pointer to an array of characters. * On success, the requested env. * variable is written to this array. * callback_status Pointer to a quadword. The status * of the get_env console callback * is returned in this quadword. * * OUTPUTS * SS$_NORMAL The callback executed, the actual * status of the callback returned * in callback_status. */ { uint64 status; HWRPB_CRB *crb; uint64 (* dispatch)(); crb = (HWRPB_CRB *) ((int)exe$gpq_hwrpb + exe$gpq_hwrpb->hwrpb$iq_crb_offset); dispatch = (uint64 (*)()) crb->hwrpb_crb$il_va_dispatch_pd_l; /* Execute get_env console callback to read the given variable */ if (env_number > 0) { /* This feeds given plain number. E.g. gives back SCSI 0 17 0 0 for "bootdef_dev"s number 3 */ status = (uint64) dispatch (HWRPB_CRB$K_GET_ENV, env_number, buffer, BUF$K_LENGTH); } else { /* This feeds given string. E.g. gives back dka100 for "bootdef_dev" */ status = (uint64) dispatch (HWRPB_CRB$K_GET_ENV, env_string, buffer, BUF$K_LENGTH); } *callback_status = status; return SS$_NORMAL; } uint64 set_env (int env_number, char *buffer, uint64 *callback_status) /* * Routine set_env -- performs set_env console callback * to write specified environment variable * * INPUTS * env_number environment variable to be written * buffer pointer to an array of characters. * On success, this string is written * to the specified EV. * callback_status Pointer to a quadword. The status * of the get_env console callback * is returned in this quadword. * * OUTPUTS * SS$_NORMAL The callback executed, the actual * status of the callback returned * in callback_status. */ { uint64 status; HWRPB_CRB *crb; uint64 (* dispatch)(); crb = (HWRPB_CRB *) ((int)exe$gpq_hwrpb + exe$gpq_hwrpb->hwrpb$iq_crb_offset); dispatch = (uint64 (*)()) crb->hwrpb_crb$il_va_dispatch_pd_l; status = (uint64) dispatch (HWRPB_CRB$K_SET_ENV, env_strings [env_number], buffer, strlen (buffer)); *callback_status = status; return SS$_NORMAL; }