1/*++ 2/* NAME 3/* mail_version 3 4/* SUMMARY 5/* time-dependent probe sender addresses 6/* SYNOPSIS 7/* #include <mail_version.h> 8/* 9/* typedef struct { 10/* char *program; /* postfix */ 11/* int major; /* 2 */ 12/* int minor; /* 9 */ 13/* int patch; /* patchlevel or -1 */ 14/* char *snapshot; /* null or snapshot info */ 15/* } MAIL_VERSION; 16/* 17/* MAIL_VERSION *mail_version_parse(version_string, why) 18/* const char *version_string; 19/* const char **why; 20/* 21/* void mail_version_free(mp) 22/* MAIL_VERSION *mp; 23/* 24/* const char *get_mail_version() 25/* 26/* int check_mail_version(version_string) 27/* const char *version_string; 28/* DESCRIPTION 29/* This module understands the format of Postfix version strings 30/* (for example the default value of "mail_version"), and 31/* provides support to compare the compile-time version of a 32/* Postfix program with the run-time version of a Postfix 33/* library. Apparently, some distributions don't use proper 34/* so-number versioning, causing programs to fail erratically 35/* after an update replaces the library but not the program. 36/* 37/* A Postfix version string consists of two or three parts 38/* separated by a single "-" character: 39/* .IP \(bu 40/* The first part is a string with the program name. 41/* .IP \(bu 42/* The second part is the program version: either two or three 43/* non-negative integer numbers separated by single "." 44/* character. Stable releases have a major version, minor 45/* version and patchlevel; experimental releases (snapshots) 46/* have only major and minor version numbers. 47/* .IP \(bu 48/* The third part is ignored with a stable release, otherwise 49/* it is a string with the snapshot release date plus some 50/* optional information. 51/* 52/* mail_version_parse() parses a version string. 53/* 54/* get_mail_version() returns the version string (the value 55/* of DEF_MAIL_VERSION) that is compiled into the library. 56/* 57/* check_mail_version() compares the caller's version string 58/* (usually the value of DEF_MAIL_VERSION) that is compiled 59/* into the caller, and logs a warning when the strings differ. 60/* DIAGNOSTICS 61/* In the case of a parsing error, mail_version_parse() returns 62/* a null pointer, and sets the why argument to a string with 63/* problem details. 64/* LICENSE 65/* .ad 66/* .fi 67/* The Secure Mailer license must be distributed with this software. 68/* AUTHOR(S) 69/* Wietse Venema 70/* IBM T.J. Watson Research 71/* P.O. Box 704 72/* Yorktown Heights, NY 10598, USA 73/*--*/ 74 75/* System library. */ 76 77#include <sys_defs.h> 78#include <stdlib.h> 79#include <errno.h> 80 81/* Utility library. */ 82 83#include <msg.h> 84#include <mymalloc.h> 85#include <stringops.h> 86#include <split_at.h> 87 88/* Global library. */ 89 90#include <mail_version.h> 91 92/* mail_version_int - convert integer */ 93 94static int mail_version_int(const char *strval) 95{ 96 char *end; 97 int intval; 98 long longval; 99 100 errno = 0; 101 intval = longval = strtol(strval, &end, 10); 102 if (*strval == 0 || *end != 0 || errno == ERANGE || longval != intval) 103 intval = (-1); 104 return (intval); 105} 106 107/* mail_version_worker - do the parsing work */ 108 109static const char *mail_version_worker(MAIL_VERSION *mp, char *cp) 110{ 111 char *major_field; 112 char *minor_field; 113 char *patch_field; 114 115 /* 116 * Program name. 117 */ 118 if ((mp->program = mystrtok(&cp, "-")) == 0) 119 return ("no program name"); 120 121 /* 122 * Major, minor, patchlevel. If this is a stable release, then we ignore 123 * text after the patchlevel, in case there are vendor extensions. 124 */ 125 if ((major_field = mystrtok(&cp, "-")) == 0) 126 return ("missing major version"); 127 128 if ((minor_field = split_at(major_field, '.')) == 0) 129 return ("missing minor version"); 130 if ((mp->major = mail_version_int(major_field)) < 0) 131 return ("bad major version"); 132 patch_field = split_at(minor_field, '.'); 133 if ((mp->minor = mail_version_int(minor_field)) < 0) 134 return ("bad minor version"); 135 136 if (patch_field == 0) 137 mp->patch = -1; 138 else if ((mp->patch = mail_version_int(patch_field)) < 0) 139 return ("bad patchlevel"); 140 141 /* 142 * Experimental release. If this is not a stable release, we take 143 * everything to the end of the string. 144 */ 145 if (patch_field != 0) 146 mp->snapshot = 0; 147 else if ((mp->snapshot = mystrtok(&cp, "")) == 0) 148 return ("missing snapshot field"); 149 150 return (0); 151} 152 153/* mail_version_parse - driver */ 154 155MAIL_VERSION *mail_version_parse(const char *string, const char **why) 156{ 157 MAIL_VERSION *mp; 158 char *saved_string; 159 const char *err; 160 161 mp = (MAIL_VERSION *) mymalloc(sizeof(*mp)); 162 saved_string = mystrdup(string); 163 if ((err = mail_version_worker(mp, saved_string)) != 0) { 164 *why = err; 165 myfree(saved_string); 166 myfree((char *) mp); 167 return (0); 168 } else { 169 return (mp); 170 } 171} 172 173/* mail_version_free - destroy version information */ 174 175void mail_version_free(MAIL_VERSION *mp) 176{ 177 myfree(mp->program); 178 myfree((char *) mp); 179} 180 181/* get_mail_version - return parsed mail version string */ 182 183const char *get_mail_version(void) 184{ 185 return (DEF_MAIL_VERSION); 186} 187 188/* check_mail_version - compare caller version with library version */ 189 190void check_mail_version(const char *version_string) 191{ 192 if (strcmp(version_string, DEF_MAIL_VERSION) != 0) 193 msg_warn("Postfix library version mis-match: wanted %s, found %s", 194 version_string, DEF_MAIL_VERSION); 195} 196 197#ifdef TEST 198 199#include <unistd.h> 200#include <vstring.h> 201#include <vstream.h> 202#include <vstring_vstream.h> 203 204#define STR(x) vstring_str(x) 205 206/* parse_sample - parse a sample string from argv or stdin */ 207 208static void parse_sample(const char *sample) 209{ 210 MAIL_VERSION *mp; 211 const char *why; 212 213 mp = mail_version_parse(sample, &why); 214 if (mp == 0) { 215 vstream_printf("ERROR: %s: %s\n", sample, why); 216 } else { 217 vstream_printf("program: %s\t", mp->program); 218 vstream_printf("major: %d\t", mp->major); 219 vstream_printf("minor: %d\t", mp->minor); 220 if (mp->patch < 0) 221 vstream_printf("snapshot: %s\n", mp->snapshot); 222 else 223 vstream_printf("patch: %d\n", mp->patch); 224 mail_version_free(mp); 225 } 226 vstream_fflush(VSTREAM_OUT); 227} 228 229/* main - the main program */ 230 231int main(int argc, char **argv) 232{ 233 VSTRING *inbuf = vstring_alloc(1); 234 int have_tty = isatty(0); 235 236 if (argc > 1) { 237 while (--argc > 0 && *++argv) 238 parse_sample(*argv); 239 } else { 240 for (;;) { 241 if (have_tty) { 242 vstream_printf("> "); 243 vstream_fflush(VSTREAM_OUT); 244 } 245 if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0) 246 break; 247 if (have_tty == 0) 248 vstream_printf("> %s\n", STR(inbuf)); 249 if (*STR(inbuf) == 0 || *STR(inbuf) == '#') 250 continue; 251 parse_sample(STR(inbuf)); 252 } 253 } 254 vstring_free(inbuf); 255 return (0); 256} 257 258#endif 259