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