Table of Contents Previous Chapter
Chapter 6: Creating Your Own Snapshot Files

Chapter 6: Creating Your Own Snapshot Files

It was generally intended that users of BURT create their own request and dependency files and that BURT would create all the snapshot files. After all, BURT is a backup and restore tool, so it is not surprising to expect that a snapshot file that is being restored is the result of some previous backup, and therefore created by BURT. However, we recognize that this is not always the case and that it is possible to use BURT to write values, in the form of snapshot files, that were not the result of a previous backup, but rather the output from some program outside BURT's suite of programs. It is for this reason that we describe the steps necessary to facilitate those sophisticated users who wish to create their own snapshot files.

We provide this description with the following warning; users wishing to create their own snapshot files should only create SDDS snapshot files. Further, continued support for those users wishing to create their own snapshot files will come in the form of a C header file called burtpublic.h (explained below in section 1). Users who create their own non-SDDS snapshot files do so at their own risk. We provide no support for those users that choose to create their own non-SDDS snapshot files, and further, we reserve the right to change any or all of the syntax/format of non-SDDS snapshot files without any consideration for such users.

1. Getting Started - Including burtpublic.h

Users wishing to create their own snapshot files will be affected by any changes made in the format and/or syntax of those files, or changes in the names of the SDDS columns and parameters of those files. In order to minimize the users' grief caused by these changes, we made a portion of BURT's internal names available to the user community in the file burtpublic.h, which presumably can be found with the rest of BURT's source code. Contact your EPICS system administrator to find out where this file resides.

burtpublic.h contains all the column and parameter names as well as some other standard values all in the form of #define macros. Users wishing to create their own SDDS snapshot files are best shielded from any changes to these names and standard values by producing these files with a C program and using the #include facility to include burtpublic.h. The intention here is that, although we may change the column and parameter names found in SDDS snapshot files, it is unlikely that we will ever change the #define'd names.

2. Parameters

Snapshot files are comprised of two parts: the header section that identifies who took the snapshot, when the snapshot was taken, and what type of snapshot it is, and the data section where the process variable and device names along with their associated values appear. In SDDS snapshot files, the header section appears at the top of the file in the form of SDDS fixed parameters. Below is a table describing all the parameters BURT looks for in a snapshot file. All parameters are strings.

-----------------------------------------------------------------------------------------
Public Names          Parameter     Required /  Values                   Default Values    
                      Names         Optional                                               
-----------------------------------------------------------------------------------------
TIMEHEADERSTRING      TimeStamp     optional    when snapshot was pro    1/1/70 00:00 GMT  
                                                duced                                      
LOGINHEADERSTRING     LoginId       optional    loginid and realworld    UNKNOWNLOGINID    
                                                name of user producing   (UNKNOWNREAL      
                                                snapshot                 WORLDNAME)        
EFFUIDHEADERSTRING    EffectiveUID  optional    effective UID of user    UNKNOWN           
                                                producing snapshot                         
GROUPIDHEADERSTRING   GroupID       optional    group ID of user pro     UNKNOWN           
                                                ducing snapshot                            
KEYWORDSHEADERSTRING  BurtKeywords  optional    user supplied keywords   empty string      
COMMENTSHEADERSTRING  BurtComments  optional    user supplied comments   empty string      
TYPEHEADERSTRING      SnapType      required    type of snapshot,                          
                                                ABSOLUTESTRING,                            
                                                RELATIVESTRING,                            
                                                or NOWRITESTRING                           
-----------------------------------------------------------------------------------------
Parameter Names are the names of the parameters as they currently (with respect to the creation of this document) appear. These names may change over time. Public Names are the names of the parameters as they appear in burtpublic.h, names that are far less likely to change and users are therefore recommended to use these names instead of the ones found in Parameter Names.

All the parameters are strings and are defined as fixed values that are intended to pertain to the entire snapshot file. Note that all but one of the parameters are optional. The only required parameter is TYPEHEADERSTRING (SnapType). However, when supplying an optional parameter, the user must be very careful to format its value carefully. Examples of how to format each of the parameters correctly appears in the example in section 4.

The values of the parameters KEYWORDSHEADERSTING (BurtKeywords), COMMENTSHEADERSTRING (BurtComments), and TYPEHEADERSTRING (SnapType) are specified directly by the user. The values of the other parameters, TIMEHEADERSTRING (TimeStamp), LOGINHEADERSTRING (LoginID), EFFUIDHEADERSTRING (EffectiveUID), and GROUPIDHEADERSTRING (GroupID) are also supplied by the user, but only after making calls to operating system, i.e., UNIX. This is illustrated by the example in section 4.

3. Columns

The second portion of snapshot files is the data. In SDDS snapshot files the data appears as a single SDDS table. Below is a table describing the columns in that SDDS snapshot table.

--------------------------------------------------------------------------------------------------
Public Names  Column       SDDS Types   Required/  Contents                Default  Default         
              Names                     Optional                           Values   Meanings        
--------------------------------------------------------------------------------------------------
NAME_COL      ControlName  SDDS_STRING  required   PV or Device name                                
TYPE_COL      ControlType  SDDS_STRING  required   type of entity,                                  
                                                   PVSTRING or                                      
                                                   DEVSTRING                                        
LINEAGE_COL   Lineage      SDDS_STRING  required   lineage of composite                             
                                                   devices, for devices                             
                                                   only, DEFAULT                                    
                                                   STRING for pvars                                 
NELEM_COL     Count        SDDS_LONG    required   number of elements, 1                            
                                                   for devices                                      
VAL_COL       ValueString  SDDS_STRING  required   value                                            
MODE_COL      ControlMode  SDDS_STRING  optional   read only tag,          DEFAULT  restore to IOC  
                                                   DERAULTSTRING,          STRING                   
                                                   READONLYSTRING,                                  
                                                   or READONLYNOTI                                  
                                                   FYSTRING                                         
READMSG_COL   BackupMsg    SDDS_STRING  optional   device read msg,        DEFAULT  DEFAULT         
                                                   DEFAULTSTRING for       STRING   READMSG         
                                                   pvars                                            
WRITEMSG_COL  RestoreMsg   SDDS_STRING  optional   device write msg,       DEFAULT  DEFAULT         
                                                   DEFAULTSTRING for       STRING   WRITEMSG        
                                                   pvars                                            
--------------------------------------------------------------------------------------------------
Similar to the parameter names, Column Names are the names of the columns as they currently appear in the SDDS snapshot files. These names may change over time. Public Names are the names of the columns as they appear in burtpublic.h, names that are far less likely to change and users are therefore recommended to use these names instead of the ones found in Column Names.

Note that all but three of the columns are required. Additionally, values of certain columns are dictated by values in other columns. For example, NELEM_COL (Count) must be 0 for devices and the columns LINEAGE_COL (Lineage), READMSG_COL (BackupMsg), and WRITEMSG_COL (RestoreMsg) must all be DEFAULT_STRING (currently defined as ``-'' in burtpublic.h) for process variables.

4. Example

In this section we present an example C program, snap.c, and that generates an SDDS snapshot file. In doing so, it does not leave out any of the optional columns or parameters. It points out which UNIX header files to include and how to retrieve and correctly format values from UNIX. It also illustrates how to correctly use the #define'd values from burtpublic.h to create the parameter and column names as well as how to define the lineage for devices and specify missing values in vectors. The program is presented with commentary. This program (without commentary) as well as its associated makefile and output file all appear as appendices to this document.

4.1. Include Files

/* for getpwuid() */
#include <pwd.h>

/* for time() */
#include <sys/types.h>
#include <sys/time.h>

/* for geteuid() */
#include <unistd.h>

#include <burtpublic.h>

#include <SDDS.h>
Initially we have the #include files. The first four are necessary for the subsequent UNIX calls we need to make to get information about the user generating the snapshot and the time at which the snapshot was generated. The last two are necessary for BURT and SDDS, respectively.

4.2. main()

main()
{
SDDS_TABLE table;
int effective_uid;
time_t currtime;
struct passwd *pp;
char buff[100];

  if (!SDDS_InitializeOutput(&table, SDDS_ASCII, 1L, NULL, NULL, "Snapshot"))
  {
    fprintf(stderr, "ERROR: unable to initialize table.\n");
    exit(1);
  } /* endif */
Above we have named the output file Snapshot. Below we define the snapshot header,i.e., the fixed valued parameters. Recall that BURT requires that only one of the parameters be defined, TYPEHEADERSTRING (SnapType). However, we have defined all for this program. First, we need to make some UNIX calls.

4.3. Necessary UNIX calls

  /***************************/
  /*                         */
  /* Defining the Parameters */
  /*                         */
  /***************************/
  /* first, make all the necessary UNIX calls */

  if (time(&currtime) == (time_t) -1)
  {
    fprintf(stderr, "ERROR: unable to get current time.\n");
    exit(1);
  } /* endif */

  effective_uid = geteuid();

  if ((pp = getpwuid(effective_uid)) == NULL)
  {
    fprintf(stderr, "ERROR: unable to get user information.\n");
    exit(1);
  } /* endif */
Above are examples of the UNIX calls necessary identify who is creating the snapshot file and the time at which it is created. Following are the SDDS calls to create each of the fixed valued parameters. Note that the public names of the parameters, i.e., those found in burtpublic.h, are used and that each parameter has been defined as the correct SDDS type. Note also the format of the parameters, particularly the format of TIMEHEADERSTRING (TimeStamp) and LOGINHEADERSTING (LoginID). BURT expects and demands this exact syntax.

4.4. Fixed Value Parameters

  /* time stamp */
  if (!SDDS_DefineParameter(&table, TIMEHEADERSTRING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, ctime(&currtime)) == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n", 
      TIMEHEADERSTRING);
    exit(1);
  } /* endif */

  /* loginid (real world name) */
  sprintf(buff, "%s (%s)", pp->pw_name, pp->pw_gecos);
  if (!SDDS_DefineParameter(&table, LOGINHEADERSTING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, buff) == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n",
      LOGINHEADERSTING);
    exit(1);
  } /* endif */

  /* effective uid */
  /* uid - person who logged in ... effective uid person as */
  /* defined by "set-user-ID" (if done at all)              */
  sprintf(buff, "%d", (int) pp->pw_uid);
  if (!SDDS_DefineParameter(&table, EFFUIDHEADERSTRING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, buff) == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n",
      EFFUIDHEADERSTRING);
    exit(1);
  } /* endif */

  /* group id */
  sprintf(buff, "%d", (int) pp->pw_gid);
  if (!SDDS_DefineParameter(&table, GROUPIDHEADERSTRING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, buff) == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n",
      GROUPIDHEADERSTRING);
    exit(1);
  } /* endif */

  /* keywords */
  if (!SDDS_DefineParameter(&table, KEYWORDSHEADERSTRING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, "these are keywords") == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n",
      KEYWORDSHEADERSTRING);
    exit(1);
    } /* endif */

  /* comments */
  if (!SDDS_DefineParameter(&table, COMMENTSHEADERSTRING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, "these are comments") == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n",
      COMMENTSHEADERSTRING);
    exit(1);
  } /* endif */

  /* snapshot type, Absolute */
  if (!SDDS_DefineParameter(&table, TYPEHEADERSTRING, 
    NULL, NULL, NULL, NULL, SDDS_STRING, ABSOLUTESTRING) == -1)
  {
    fprintf(stderr, "ERROR: could not define parameter >%s<.\n",
      TYPEHEADERSTRING);
    exit(1);
  } /* endif */
We have chosen to create an Absolute snapshot file by specifying the value ABSOLUTESTRING. We could have just as easily created a Relative or Nowrite snapshot file.

Immediately following is the definition of the columns. Although BURT does not require all the columns, we chose to include all of them. Note that each column has been defined with the correct public name and as the correct SDDS type.

4.5. Columns

    /************************/
    /*                      */
    /* Defining the Columns */
    /*                      */
    /************************/

  if (SDDS_DefineColumn(&table, NAME_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", NAME_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, TYPE_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", TYPE_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, LINEAGE_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", LINEAGE_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, READMSG_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", READMSG_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, WRITEMSG_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", WRITEMSG_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, MODE_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", MODE_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, NELEM_COL, 
    NULL, NULL, NULL, NULL, SDDS_LONG, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", NELEM_COL);
    exit(1);
  } /* endif */

  if (SDDS_DefineColumn(&table, VAL_COL, 
    NULL, NULL, NULL, NULL, SDDS_STRING, 0) == -1)
  {
    fprintf(stderr, "ERROR: could not define column >%s<.\n", VAL_COL);
    exit(1);
  } /* endif */

4.6. Writing header

  /**********************/
  /*                    */
  /* Writing the Header */
  /*                    */
  /**********************/

  if (!SDDS_WriteLayout(&table))
  {
    fprintf(stderr, "ERROR: could not write header.\n");
    exit(1);
  } /* endif */

4.7. Starting the table

  /***************************/
  /*                         */
  /* Starting the Data Table */
  /*                         */
  /***************************/

  if (!SDDS_StartTable(&table, 5L))
  {
    fprintf(stderr, "ERROR: unable to start the data table\n");
    exit(1);
  } /* endif */
Below we fill the table with data values. This particular table has five entries, two process variables and three devices.

4.8. Filling the table

  /*******************************/
  /*                             */
  /* Filling the Table With Data */
  /*                             */
  /*******************************/
The first value is a scalar process variable, ``burtgenerator'', with a value of 0.0.

  /* row 0: scalar pv, name="burtgenerator" val=0.0 */
  if (!SDDS_SetRowValues(&table, 
    (long) (SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE), 0L,
    NAME_COL, "burtgenerator",
    TYPE_COL, PVSTRING,
    LINEAGE_COL, DEFAULTSTRING,
    READMSG_COL, DEFAULTSTRING,
    WRITEMSG_COL, DEFAULTSTRING,
    MODE_COL, DEFAULTSTRING,
    NELEM_COL, 1L,
    VAL_COL, "0.0",
    NULL))
  {
    fprintf(stderr, "ERROR: unable to set row 0\n");
    exit(1);
  } /* endif */
The second value is a device, ``burtdevao'', that is tagged Read Only and has a value of 1.0

  /* row 1: device, name="burtdevao" val=1.0 mode=RO */
  if (!SDDS_SetRowValues(&table, 
    (long) (SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE), 1L,
    NAME_COL, "burtdevao",
    TYPE_COL, DEVSTRING,
    LINEAGE_COL, DEFAULTSTRING,
    READMSG_COL, DEFAULTREADMSG,
    WRITEMSG_COL, DEFAULTWRITEMSG,
    MODE_COL, READONLYSTRING,
    NELEM_COL, 1L,
    VAL_COL, "1.0",
    NULL))
  {
    fprintf(stderr, "ERROR: unable to set row 1\n");
    exit(1);
  } /* endif */
The third value is also a device, ``burtdevcalc'', that is tagged Read Only Notify and has a value of 2.0.

  /* row 2: device, name="burtdevcalc" val=2.0 mode=RON */
  if (!SDDS_SetRowValues(&table, 
    (long) (SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE), 2L,
    NAME_COL, "burtdevcalc",
    TYPE_COL, DEVSTRING,
    LINEAGE_COL, DEFAULTSTRING,
    READMSG_COL, DEFAULTREADMSG,
    WRITEMSG_COL, DEFAULTWRITEMSG,
    MODE_COL, READONLYNOTIFYSTRING,
    NELEM_COL, 1L,
    VAL_COL, "2.0",
    NULL))
  {
    fprintf(stderr, "ERROR: unable to set row 2\n");
    exit(1);
  } /* endif */
The fourth value is a device, ``burtdevcalc'', that is part of a composite device called ``burtdevcomp''. Its value is 3.0 and it here we choose to explicitly supply its backup message, ``read''. Although its backup message happens to be the same as the DEFAULTREADMSG, we explicitly assigned the backup message its value to demonstrate how to do so. We could have just as easily chosen any backup message we wanted to.

  /* row 3: device, name="burtdevcalc" val=3.0 parent=burtdevcomp */
  /*                readmsg="read" */
  if (!SDDS_SetRowValues(&table, 
    (long) (SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE), 3L,
    NAME_COL, "burtdevcalc",
    TYPE_COL, DEVSTRING,
    LINEAGE_COL, "burtdevcomp",
    READMSG_COL, "read",
    WRITEMSG_COL, DEFAULTWRITEMSG,
    MODE_COL, DEFAULTSTRING,
    NELEM_COL, 1L,
    VAL_COL, "3.0",
    NULL))
  {
    fprintf(stderr, "ERROR: unable to set row 3\n");
    exit(1);
  } /* endif */
The fifth value is a vector process variable, ``burtwaveform'', that has six elements. The last two values of the vector are missing. Note when supplying values to a vector where some of the values are missing the user must use NULLSTRING (from burtpublic.h) to specify the missing values.

  /* row 4: vector pv, name="burtwaveform" nelem=6 */
  /*                   val=[4.0 4.1 4.2 4.3 4.4 <missing> <missing> ] */
  sprintf(buff, "4.0 4.1 4.2 4.3 %s %s", NULLSTRING, NULLSTRING);
  if (!SDDS_SetRowValues(&table, 
    (long) (SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE), 4L,
    NAME_COL, "burtwaveform",
    TYPE_COL, PVSTRING,
    LINEAGE_COL, DEFAULTSTRING,
    READMSG_COL, DEFAULTSTRING,
    WRITEMSG_COL, DEFAULTSTRING,
    MODE_COL, DEFAULTSTRING,
    NELEM_COL, 6L,
    VAL_COL, buff,
    NULL))
  {
    fprintf(stderr, "ERROR: unable to set row 4\n");
    exit(1);
  } /* endif */

4.9. Writing the table

  /**************************/
  /*                        */
  /* Writing the Data Table */
  /*                        */
  /**************************/

  if (!SDDS_WriteTable(&table))
  {
    fprintf(stderr, "ERROR: could not write table.\n");
    exit(1);
  } /* endif */

4.10. Cleanup

  /***********/
  /*         */
  /* Cleanup */
  /*         */
  /***********/

  if (!SDDS_Terminate(&table))
  {
    fprintf(stderr, "ERROR: could not terminate SDDS table.\n");
    exit(1);
  } /* endif */
} /* end main() */
As mentioned, the previous program without interleaved commentary, its makefile, and the output it generates can all be found in the appendices to this document.

There is one more note of interest which has to do with reading SDDS snapshot files into programs outside the suite of BURT's programs. As mentioned earlier, this document is not intended as an instructional aid for SDDS, however, there is one parameter in the SDDS snapshot files that merits further discussion, TIMEHEADERSTRING (TimeStamp). It has a format all its own and it is occasionally convenient to convert that string value to one of the UNIX conventions of the number of seconds since 00:00:00, January 1, 1970. This is done with the UNIX command strptime() which converts a string representation of time to the number of seconds. It expects, as one of its arguments, a description of the format of the string. This format can also be found in burtpublic.h in the #define'd variable TIMEFORMATSTRING.

Assuming that the value of the parameter has been read into a string pointed at by the variable cp, here is the code convert the string into one of UNIX's internal structures.

/* for strptime() */
#include <time.h>
#include <burtpublic.h>
main()
{
   char *cp;
   struct tm tmtime;
   .
   .
   .
   strptime(cp, TIMEFORMATSTRING, &tmtime);
   .
   .
   .
} /* end main() */
 
Table of Contents Next Chapter