1  /* Copyright (C) 1997-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <fmtmsg.h>
      19  #include <libc-lock.h>
      20  #include <stdio.h>
      21  #include <stdint.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <sys/syslog.h>
      25  #include <wchar.h>
      26  
      27  
      28  /* We have global data, protect the modification.  */
      29  __libc_lock_define_initialized (static, lock)
      30  
      31  
      32  enum
      33  {
      34    label_mask = 0x01,
      35    severity_mask = 0x02,
      36    text_mask = 0x04,
      37    action_mask = 0x08,
      38    tag_mask = 0x10,
      39    all_mask = label_mask | severity_mask | text_mask | action_mask | tag_mask
      40  };
      41  
      42  static const struct
      43  {
      44    uint32_t len;
      45    /* Adjust the size if new elements are added.  */
      46    const char name[12];
      47  } keywords[] =
      48    {
      49      { 5, "label" },
      50      { 8, "severity" },
      51      { 4, "text" },
      52      { 6, "action"},
      53      { 3, "tag" }
      54    };
      55  #define NKEYWORDS (sizeof (keywords) / sizeof (keywords[0]))
      56  
      57  
      58  struct severity_info
      59  {
      60    int severity;
      61    const char *string;
      62    struct severity_info *next;
      63  };
      64  
      65  
      66  /* List of known severities.  */
      67  static const struct severity_info nosev =
      68  {
      69    MM_NOSEV, "", NULL
      70  };
      71  static const struct severity_info haltsev =
      72  {
      73    MM_HALT, "HALT", (struct severity_info *) &nosev
      74  };
      75  static const struct severity_info errorsev =
      76  {
      77    MM_ERROR, "ERROR", (struct severity_info *) &haltsev
      78  };
      79  static const struct severity_info warningsev =
      80  {
      81    MM_WARNING, "WARNING", (struct severity_info *) &errorsev
      82  };
      83  static const struct severity_info infosev =
      84  {
      85    MM_INFO, "INFO", (struct severity_info *) &warningsev
      86  };
      87  
      88  /* Start of the list.  */
      89  static struct severity_info *severity_list = (struct severity_info *) &infosev;
      90  
      91  /* Mask of values we will print.  */
      92  static int print;
      93  
      94  /* Prototypes for local functions.  */
      95  static void init (void);
      96  static int internal_addseverity (int severity, const char *string);
      97  
      98  
      99  int
     100  fmtmsg (long int classification, const char *label, int severity,
     101  	const char *text, const char *action, const char *tag)
     102  {
     103    __libc_once_define (static, once);
     104    struct severity_info *severity_rec;
     105  
     106    /* Make sure everything is initialized.  */
     107    __libc_once (once, init);
     108  
     109    /* Start the real work.  First check whether the input is ok.  */
     110    if (label != MM_NULLLBL)
     111      {
     112        /* Must be two fields, separated by a colon.  */
     113        const char *cp = strchr (label, ':');
     114        if (cp == NULL)
     115  	return MM_NOTOK;
     116  
     117        /* The first field must not contain more than 10 bytes.  */
     118        if (cp - label > 10
     119  	  /* The second field must not have more than 14 bytes.  */
     120  	  || strlen (cp + 1) > 14)
     121  	return MM_NOTOK;
     122      }
     123  
     124    /* We do not want this call to be cut short by a thread
     125       cancellation.  Therefore disable cancellation for now.  */
     126    int state = PTHREAD_CANCEL_ENABLE;
     127    __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
     128  
     129    __libc_lock_lock (lock);
     130  
     131    for (severity_rec = severity_list; severity_rec != NULL;
     132         severity_rec = severity_rec->next)
     133      if (severity == severity_rec->severity)
     134        /* Bingo.  */
     135        break;
     136  
     137    /* If we don't know anything about the severity level return an error.  */
     138    int result = MM_NOTOK;
     139    if (severity_rec != NULL)
     140      {
     141        result = MM_OK;
     142  
     143        /* Now we can print.  */
     144        if (classification & MM_PRINT)
     145  	{
     146  	  int do_label = (print & label_mask) && label != MM_NULLLBL;
     147  	  int do_severity = (print & severity_mask) && severity != MM_NULLSEV;
     148  	  int do_text = (print & text_mask) && text != MM_NULLTXT;
     149  	  int do_action = (print & action_mask) && action != MM_NULLACT;
     150  	  int do_tag = (print & tag_mask) && tag != MM_NULLTAG;
     151  	  int need_colon = (do_label
     152  			    && (do_severity | do_text | do_action | do_tag));
     153  
     154  	  if (__fxprintf (stderr, "%s%s%s%s%s%s%s%s%s%s\n",
     155  			  do_label ? label : "",
     156  			  need_colon ? ": " : "",
     157  			  do_severity ? severity_rec->string : "",
     158  			  do_severity && (do_text | do_action | do_tag)
     159  			  ? ": " : "",
     160  			  do_text ? text : "",
     161  			  do_text && (do_action | do_tag) ? "\n" : "",
     162  			  do_action ? "TO FIX: " : "",
     163  			  do_action ? action : "",
     164  			  do_action && do_tag ? "  " : "",
     165  			  do_tag ? tag : "") < 0)
     166  	    /* Oh, oh.  An error occurred during the output.  */
     167  	    result = MM_NOMSG;
     168  	}
     169  
     170        if (classification & MM_CONSOLE)
     171  	{
     172  	  int do_label = label != MM_NULLLBL;
     173  	  int do_severity = severity != MM_NULLSEV;
     174  	  int do_text = text != MM_NULLTXT;
     175  	  int do_action = action != MM_NULLACT;
     176  	  int do_tag = tag != MM_NULLTAG;
     177  	  int need_colon = (do_label
     178  			    && (do_severity | do_text | do_action | do_tag));
     179  
     180  	  syslog (LOG_ERR, "%s%s%s%s%s%s%s%s%s%s\n",
     181  		  do_label ? label : "",
     182  		  need_colon ? ": " : "",
     183  		  do_severity ? severity_rec->string : "",
     184  		  do_severity && (do_text | do_action | do_tag) ? ": " : "",
     185  		  do_text ? text : "",
     186  		  do_text && (do_action | do_tag) ? "\n" : "",
     187  		  do_action ? "TO FIX: " : "",
     188  		  do_action ? action : "",
     189  		  do_action && do_tag ? "  " : "",
     190  		  do_tag ? tag : "");
     191  	}
     192      }
     193  
     194    __libc_lock_unlock (lock);
     195  
     196    __pthread_setcancelstate (state, NULL);
     197  
     198    return result;
     199  }
     200  
     201  
     202  /* Initialize from environment variable content.  */
     203  static void
     204  init (void)
     205  {
     206    const char *msgverb_var = getenv ("MSGVERB");
     207    const char *sevlevel_var = getenv ("SEV_LEVEL");
     208  
     209    if (msgverb_var != NULL && msgverb_var[0] != '\0')
     210      {
     211        /* Using this extra variable allows us to work without locking.  */
     212        do
     213  	{
     214  	  size_t cnt;
     215  
     216  	  for (cnt = 0; cnt < NKEYWORDS; ++cnt)
     217  	    if (memcmp (msgverb_var,
     218  			keywords[cnt].name, keywords[cnt].len) == 0
     219  		&& (msgverb_var[keywords[cnt].len] == ':'
     220  		    || msgverb_var[keywords[cnt].len] == '\0'))
     221  	      break;
     222  
     223  	  if (cnt < NKEYWORDS)
     224  	    {
     225  	      print |= 1 << cnt;
     226  
     227  	      msgverb_var += keywords[cnt].len;
     228  	      if (msgverb_var[0] == ':')
     229  		++msgverb_var;
     230  	    }
     231  	  else
     232  	    {
     233  	      /* We found an illegal keyword in the environment
     234  		 variable.  The specifications say that we print all
     235  		 fields.  */
     236  	      print = all_mask;
     237  	      break;
     238  	    }
     239  	}
     240        while (msgverb_var[0] != '\0');
     241      }
     242    else
     243      print = all_mask;
     244  
     245  
     246    if (sevlevel_var != NULL)
     247      {
     248        __libc_lock_lock (lock);
     249  
     250        while (sevlevel_var[0] != '\0')
     251  	{
     252  	  const char *end = __strchrnul (sevlevel_var, ':');
     253  	  int level;
     254  
     255  	  /* First field: keyword.  This is not used here but it must be
     256  	     present.  */
     257  	  while (sevlevel_var < end)
     258  	    if (*sevlevel_var++ == ',')
     259  	      break;
     260  
     261  	  if (sevlevel_var < end)
     262  	    {
     263  	      /* Second field: severity level, a number.  */
     264  	      char *cp;
     265  
     266  	      level = strtol (sevlevel_var, &cp, 0);
     267  	      if (cp != sevlevel_var && cp < end && *cp++ == ','
     268  		  && level > MM_INFO)
     269  		{
     270  		  const char *new_string;
     271  
     272  		  new_string = __strndup (cp, end - cp);
     273  
     274  		  if (new_string != NULL
     275  		      && (internal_addseverity (level, new_string)
     276  			  != MM_OK))
     277  		    free ((char *) new_string);
     278  		}
     279  	    }
     280  
     281  	  sevlevel_var = end + (*end == ':' ? 1 : 0);
     282  	}
     283  
     284        __libc_lock_unlock (lock);
     285      }
     286  }
     287  
     288  
     289  /* Add the new entry to the list.  */
     290  static int
     291  internal_addseverity (int severity, const char *string)
     292  {
     293    struct severity_info *runp, *lastp;
     294    int result = MM_OK;
     295  
     296    /* First see if there is already a record for the severity level.  */
     297    for (runp = severity_list, lastp = NULL; runp != NULL; runp = runp->next)
     298      if (runp->severity == severity)
     299        break;
     300      else
     301        lastp = runp;
     302  
     303    if (runp != NULL)
     304      {
     305        if (string != NULL)
     306  	/* Change the string.  */
     307  	runp->string = string;
     308        else
     309  	{
     310  	  /* Remove the severity class.  */
     311  	  if (lastp == NULL)
     312  	    severity_list = runp->next;
     313  	  else
     314  	    lastp->next = runp->next;
     315  
     316  	  free (runp);
     317  	}
     318      }
     319    else if (string != NULL)
     320      {
     321        runp = malloc (sizeof (*runp));
     322        if (runp == NULL)
     323  	result = MM_NOTOK;
     324        else
     325  	{
     326  	  runp->severity = severity;
     327  	  runp->next = severity_list;
     328  	  runp->string = string;
     329  	  severity_list = runp;
     330  	}
     331      }
     332    else
     333      /* We tried to remove a non-existing severity class.  */
     334      result = MM_NOTOK;
     335  
     336    return result;
     337  }
     338  
     339  
     340  /* Add new severity level or remove old one.  */
     341  int
     342  __addseverity (int severity, const char *string)
     343  {
     344    int result;
     345  
     346    /* Prevent illegal SEVERITY values.  */
     347    if (severity <= MM_INFO)
     348      return MM_NOTOK;
     349  
     350    /* Protect the global data.  */
     351    __libc_lock_lock (lock);
     352  
     353    /* Do the real work.  */
     354    result = internal_addseverity (severity, string);
     355  
     356    /* Release the lock.  */
     357    __libc_lock_unlock (lock);
     358  
     359    return result;
     360  }
     361  weak_alias (__addseverity, addseverity)
     362  
     363  
     364  void
     365  __libc_fmtmsg_freemem (void)
     366  {
     367    struct severity_info *runp = severity_list;
     368  
     369    while (runp != NULL)
     370      if (runp->severity > MM_INFO)
     371        {
     372  	/* This is data we have to release.  */
     373  	struct severity_info *here = runp;
     374  	runp = runp->next;
     375  	free (here);
     376        }
     377      else
     378        runp = runp->next;
     379  }