]> granicus.if.org Git - postgresql/blob - src/backend/libpq/pqcomm.c
getpid/pid cleanup
[postgresql] / src / backend / libpq / pqcomm.c
1  /*-------------------------------------------------------------------------
2  *
3  * pqcomm.c--
4  *        Communication functions between the Frontend and the Backend
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.34 1998/01/25 05:13:18 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 /*
15  * INTERFACE ROUTINES
16  *              pq_gettty               - return the name of the tty in the given buffer
17  *              pq_getport              - return the PGPORT setting
18  *              pq_close                - close input / output connections
19  *              pq_flush                - flush pending output
20  *              pq_getstr               - get a null terminated string from connection
21  *              pq_getnchar             - get n characters from connection
22  *              pq_getint               - get an integer from connection
23  *              pq_putstr               - send a null terminated string to connection
24  *              pq_putnchar             - send n characters to connection
25  *              pq_putint               - send an integer to connection
26  *              pq_getinaddr    - initialize address from host and port number
27  *              pq_getinserv    - initialize address from host and service name
28  *              pq_connect              - create remote input / output connection
29  *              pq_accept               - accept remote input / output connection
30  *              pq_async_notify - receive notification from backend.
31  *
32  * NOTES
33  *              These functions are used by both frontend applications and
34  *              the postgres backend.
35  *
36  */
37 #include <stdio.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <unistd.h>                             /* for ttyname() */
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/socket.h>
46 #include <netdb.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
50
51 #if defined(linux)
52 #ifndef SOMAXCONN
53 #define SOMAXCONN 5                             /* from Linux listen(2) man page */
54 #endif                                                  /* SOMAXCONN */
55 #endif                                                  /* linux */
56
57 #include <postgres.h>
58
59 #include <miscadmin.h>
60 #include <libpq/pqsignal.h>
61 #include <libpq/auth.h>
62 #include <libpq/libpq.h>                /* where the declarations go */
63 #include <storage/ipc.h>
64
65 /* ----------------
66  *              declarations
67  * ----------------
68  */
69 FILE       *Pfout,
70                    *Pfin;
71 FILE       *Pfdebug;                    /* debugging libpq */
72 int                     PQAsyncNotifyWaiting;           /* for async. notification */
73
74 /* --------------------------------
75  *              pq_init - open portal file descriptors
76  * --------------------------------
77  */
78 void
79 pq_init(int fd)
80 {
81         Pfin = fdopen(fd, "r");
82         Pfout = fdopen(dup(fd), "w");
83         if (!Pfin || !Pfout)
84                 elog(FATAL, "pq_init: Couldn't initialize socket connection");
85         PQnotifies_init();
86         if (getenv("LIBPQ_DEBUG"))
87         {
88                 Pfdebug = stderr;
89         }
90         else
91         {
92                 Pfdebug = NULL;
93         }
94 }
95
96 /* -------------------------
97  *       pq_getc(File* fin)
98  *
99  *       get a character from the input file,
100  *
101  *       if Pfdebug is set, also echo the character fetched into Pfdebug
102  *
103  *       used for debugging libpq
104  */
105 static int
106 pq_getc(FILE *fin)
107 {
108         int                     c;
109
110         c = getc(fin);
111         if (Pfdebug && c != EOF)
112                 putc(c, Pfdebug);
113         return c;
114 }
115
116 /* --------------------------------
117  *              pq_gettty - return the name of the tty in the given buffer
118  * --------------------------------
119  */
120 void
121 pq_gettty(char *tp)
122 {
123         strncpy(tp, ttyname(0), 19);
124 }
125
126 /* --------------------------------
127  *              pq_getport - return the PGPORT setting
128  * --------------------------------
129  */
130 int
131 pq_getport()
132 {
133         char       *envport = getenv("PGPORT");
134
135         if (envport)
136                 return (atoi(envport));
137         return (atoi(DEF_PGPORT));
138 }
139
140 /* --------------------------------
141  *              pq_close - close input / output connections
142  * --------------------------------
143  */
144 void
145 pq_close()
146 {
147         if (Pfin)
148         {
149                 fclose(Pfin);
150                 Pfin = NULL;
151         }
152         if (Pfout)
153         {
154                 fclose(Pfout);
155                 Pfout = NULL;
156         }
157         PQAsyncNotifyWaiting = 0;
158         PQnotifies_init();
159         pq_unregoob();
160 }
161
162 /* --------------------------------
163  *              pq_flush - flush pending output
164  * --------------------------------
165  */
166 void
167 pq_flush()
168 {
169         if (Pfout)
170                 fflush(Pfout);
171 }
172
173 /* --------------------------------
174  *              pq_getstr - get a null terminated string from connection
175  * --------------------------------
176  */
177 int
178 pq_getstr(char *s, int maxlen)
179 {
180         int                     c = '\0';
181
182         if (Pfin == (FILE *) NULL)
183         {
184 /*              elog(DEBUG, "Input descriptor is null"); */
185                 return (EOF);
186         }
187
188         while (maxlen-- && (c = pq_getc(Pfin)) != EOF && c)
189                 *s++ = c;
190         *s = '\0';
191
192         /* -----------------
193          *         If EOF reached let caller know.
194          *         (This will only happen if we hit EOF before the string
195          *         delimiter is reached.)
196          * -----------------
197          */
198         if (c == EOF)
199                 return (EOF);
200         return (!EOF);
201 }
202
203 /*
204  * USER FUNCTION - gets a newline-terminated string from the backend.
205  *
206  * Chiefly here so that applications can use "COPY <rel> to stdout"
207  * and read the output string.  Returns a null-terminated string in s.
208  *
209  * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
210  * the terminating \n (like gets(3)).
211  *
212  * RETURNS:
213  *              EOF if it is detected or invalid arguments are given
214  *              0 if EOL is reached (i.e., \n has been read)
215  *                              (this is required for backward-compatibility -- this
216  *                               routine used to always return EOF or 0, assuming that
217  *                               the line ended within maxlen bytes.)
218  *              1 in other cases
219  */
220 int
221 PQgetline(char *s, int maxlen)
222 {
223         if (!Pfin || !s || maxlen <= 1)
224                 return (EOF);
225
226         if (fgets(s, maxlen - 1, Pfin) == NULL)
227         {
228                 return feof(Pfin) ? EOF : 1;
229         }
230         else
231         {
232                 for (; *s; s++)
233                 {
234                         if (*s == '\n')
235                         {
236                                 *s = '\0';
237                                 break;
238                         }
239                 }
240         }
241
242         return 0;
243 }
244
245 /*
246  * USER FUNCTION - sends a string to the backend.
247  *
248  * Chiefly here so that applications can use "COPY <rel> from stdin".
249  *
250  * RETURNS:
251  *              0 in all cases.
252  */
253 int
254 PQputline(char *s)
255 {
256         if (Pfout)
257         {
258                 fputs(s, Pfout);
259                 fflush(Pfout);
260         }
261         return (0);
262 }
263
264 /* --------------------------------
265  *              pq_getnchar - get n characters from connection
266  * --------------------------------
267  */
268 int
269 pq_getnchar(char *s, int off, int maxlen)
270 {
271         return pqGetNBytes(s + off, maxlen, Pfin);
272
273 #if 0
274         int                     c = '\0';
275
276         if (Pfin == (FILE *) NULL)
277         {
278 /*              elog(DEBUG, "Input descriptor is null"); */
279                 return (EOF);
280         }
281
282         s += off;
283         while (maxlen-- && (c = pq_getc(Pfin)) != EOF)
284                 *s++ = c;
285
286         /* -----------------
287          *         If EOF reached let caller know
288          * -----------------
289          */
290         if (c == EOF)
291                 return (EOF);
292         return (!EOF);
293 #endif
294 }
295
296 /* --------------------------------
297  *              pq_getint - get an integer from connection
298  *       we receive an integer a byte at a type and reconstruct it so that
299  *       machines with different ENDIAN representations can talk to each
300  *       other
301  * --------------------------------
302  */
303 int
304 pq_getint(int b)
305 {
306         int                     n,
307                                 status = 1;
308
309         if (!Pfin)
310                 return EOF;
311
312         /*
313          * mjl: Seems inconsisten w/ return value of pq_putint (void). Also,
314          * EOF is a valid return value for an int! XXX
315          */
316
317         switch (b)
318         {
319                 case 1:
320                         status = ((n = fgetc(Pfin)) == EOF);
321                         break;
322                 case 2:
323                         status = pqGetShort(&n, Pfin);
324                         break;
325                 case 4:
326                         status = pqGetLong(&n, Pfin);
327                         break;
328                 default:
329                         fprintf(stderr, "** Unsupported size %d\n", b);
330         }
331
332         if (status)
333         {
334                 sprintf(PQerrormsg,
335                                 "FATAL: pq_getint failed: errno=%d\n", errno);
336                 fputs(PQerrormsg, stderr);
337                 pqdebug("%s", PQerrormsg);
338                 n = 0;
339         }
340
341         return n;
342 }
343
344 /* --------------------------------
345  *              pq_putstr - send a null terminated string to connection
346  * --------------------------------
347  */
348 void
349 pq_putstr(char *s)
350 {
351         if (pqPutString(s, Pfout))
352         {
353                 sprintf(PQerrormsg,
354                                 "FATAL: pq_putstr: fputs() failed: errno=%d\n", errno);
355                 fputs(PQerrormsg, stderr);
356                 pqdebug("%s", PQerrormsg);
357         }
358 }
359
360 /* --------------------------------
361  *              pq_putnchar - send n characters to connection
362  * --------------------------------
363  */
364 void
365 pq_putnchar(char *s, int n)
366 {
367         if (pqPutNBytes(s, n, Pfout))
368         {
369                 sprintf(PQerrormsg,
370                                 "FATAL: pq_putnchar: fputc() failed: errno=%d\n",
371                                 errno);
372                 fputs(PQerrormsg, stderr);
373                 pqdebug("%s", PQerrormsg);
374         }
375 }
376
377 /* --------------------------------
378  *              pq_putint - send an integer to connection
379  *       we chop an integer into bytes and send individual bytes
380  *       machines with different ENDIAN representations can still talk to each
381  *       other
382  * --------------------------------
383  */
384 void
385 pq_putint(int i, int b)
386 {
387         int                     status;
388
389         if (!Pfout)
390                 return;
391
392         status = 1;
393         switch (b)
394         {
395                 case 1:
396                         status = (fputc(i, Pfout) == EOF);
397                         break;
398                 case 2:
399                         status = pqPutShort(i, Pfout);
400                         break;
401                 case 4:
402                         status = pqPutLong(i, Pfout);
403                         break;
404                 default:
405                         fprintf(stderr, "** Unsupported size %d\n", b);
406         }
407
408         if (status)
409         {
410                 sprintf(PQerrormsg,
411                                 "FATAL: pq_putint failed: errno=%d\n", errno);
412                 fputs(PQerrormsg, stderr);
413                 pqdebug("%s", PQerrormsg);
414         }
415 }
416
417 /* ---
418  *         pq_sendoob - send a string over the out-of-band channel
419  *         pq_recvoob - receive a string over the oob channel
420  *      NB: Fortunately, the out-of-band channel doesn't conflict with
421  *              buffered I/O because it is separate from regular com. channel.
422  * ---
423  */
424 int
425 pq_sendoob(char *msg, int len)
426 {
427         int                     fd = fileno(Pfout);
428
429         return (send(fd, msg, len, MSG_OOB));
430 }
431
432 int
433 pq_recvoob(char *msgPtr, int *lenPtr)
434 {
435         int                     fd = fileno(Pfout);
436         int                     len = 0;
437
438         len = recv(fd, msgPtr + len, *lenPtr, MSG_OOB);
439         *lenPtr = len;
440         return (len);
441 }
442
443 /* --------------------------------
444  *              pq_getinaddr - initialize address from host and port number
445  * --------------------------------
446  */
447 int
448 pq_getinaddr(struct sockaddr_in * sin,
449                          char *host,
450                          int port)
451 {
452         struct hostent *hs;
453
454         MemSet((char *) sin, 0, sizeof(*sin));
455
456         if (host)
457         {
458                 if (*host >= '0' && *host <= '9')
459                         sin->sin_addr.s_addr = inet_addr(host);
460                 else
461                 {
462                         if (!(hs = gethostbyname(host)))
463                         {
464                                 perror(host);
465                                 return (1);
466                         }
467                         if (hs->h_addrtype != AF_INET)
468                         {
469                                 sprintf(PQerrormsg,
470                                                 "FATAL: pq_getinaddr: %s not on Internet\n",
471                                                 host);
472                                 fputs(PQerrormsg, stderr);
473                                 pqdebug("%s", PQerrormsg);
474                                 return (1);
475                         }
476                         memmove((char *) &sin->sin_addr,
477                                         hs->h_addr,
478                                         hs->h_length);
479                 }
480         }
481         sin->sin_family = AF_INET;
482         sin->sin_port = htons(port);
483         return (0);
484 }
485
486 /* --------------------------------
487  *              pq_getinserv - initialize address from host and servive name
488  * --------------------------------
489  */
490 int
491 pq_getinserv(struct sockaddr_in * sin, char *host, char *serv)
492 {
493         struct servent *ss;
494
495         if (*serv >= '0' && *serv <= '9')
496                 return (pq_getinaddr(sin, host, atoi(serv)));
497         if (!(ss = getservbyname(serv, NULL)))
498         {
499                 sprintf(PQerrormsg,
500                                 "FATAL: pq_getinserv: unknown service: %s\n",
501                                 serv);
502                 fputs(PQerrormsg, stderr);
503                 pqdebug("%s", PQerrormsg);
504                 return (1);
505         }
506         return (pq_getinaddr(sin, host, ntohs(ss->s_port)));
507 }
508
509 /*
510  * register an out-of-band listener proc--at most one allowed.
511  * This is used for receiving async. notification from the backend.
512  */
513 void
514 pq_regoob(void (*fptr) ())
515 {
516         int                     fd = fileno(Pfout);
517
518 #if defined(hpux)
519         ioctl(fd, FIOSSAIOOWN, MyProcPid);
520 #elif defined(sco)
521         ioctl(fd, SIOCSPGRP, MyProcPid);
522 #else
523         fcntl(fd, F_SETOWN, MyProcPid);
524 #endif                                                  /* hpux */
525         pqsignal(SIGURG, fptr);
526 }
527
528 void
529 pq_unregoob()
530 {
531         pqsignal(SIGURG, SIG_DFL);
532 }
533
534
535 void
536 pq_async_notify()
537 {
538         char            msg[20];
539
540         /* int len = sizeof(msg); */
541         int                     len = 20;
542
543         if (pq_recvoob(msg, &len) >= 0)
544         {
545                 /* debugging */
546                 printf("received notification: %s\n", msg);
547                 PQAsyncNotifyWaiting = 1;
548                 /* PQappendNotify(msg+1); */
549         }
550         else
551         {
552                 extern int      errno;
553
554                 printf("SIGURG but no data: len = %d, err=%d\n", len, errno);
555         }
556 }
557
558 /*
559  * Streams -- wrapper around Unix socket system calls
560  *
561  *
562  *              Stream functions are used for vanilla TCP connection protocol.
563  */
564
565 /*
566  * StreamServerPort -- open a sock stream "listening" port.
567  *
568  * This initializes the Postmaster's connection
569  *              accepting port.
570  *
571  * ASSUME: that this doesn't need to be non-blocking because
572  *              the Postmaster uses select() to tell when the socket
573  *              is ready.
574  *
575  * RETURNS: STATUS_OK or STATUS_ERROR
576  */
577
578 static char sock_path[MAXPGPATH+1] = "";
579
580 /* do_unlink()
581  * Shutdown routine for backend connection
582  * If a Unix socket is used for communication, explicitly close it.
583  */
584 static void
585 do_unlink()
586 {
587         if (sock_path[0])
588                 unlink(sock_path);
589 }
590
591 int
592 StreamServerPort(char *hostName, short portName, int *fdP)
593 {
594         union
595         {
596                 struct sockaddr_in in;
597                 struct sockaddr_un un;
598         }                       saddr;
599         int                     fd,
600                                 err,
601                                 family;
602         size_t          len;
603         int                     one = 1;
604
605         family = ((hostName != NULL) ? AF_INET : AF_UNIX);
606
607         if ((fd = socket(family, SOCK_STREAM, 0)) < 0)
608         {
609                 sprintf(PQerrormsg,
610                                 "FATAL: StreamServerPort: socket() failed: errno=%d\n",
611                                 errno);
612                 fputs(PQerrormsg, stderr);
613                 pqdebug("%s", PQerrormsg);
614                 return (STATUS_ERROR);
615         }
616         if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
617                                         sizeof(one))) == -1)
618         {
619                 sprintf(PQerrormsg,
620                                 "FATAL: StreamServerPort: setsockopt (SO_REUSEADDR) failed: errno=%d\n",
621                                 errno);
622                 fputs(PQerrormsg, stderr);
623                 pqdebug("%s", PQerrormsg);
624                 return (STATUS_ERROR);
625         }
626         bzero(&saddr, sizeof(saddr));
627         if (family == AF_UNIX)
628         {
629                 saddr.un.sun_family = family;
630                 len = UNIXSOCK_PATH(saddr.un, portName);
631                 strcpy(sock_path, saddr.un.sun_path);
632         }
633         else
634         {
635                 saddr.in.sin_family = family;
636                 saddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
637                 saddr.in.sin_port = htons(portName);
638                 len = sizeof saddr.in;
639         }
640         err = bind(fd, (struct sockaddr *) & saddr, len);
641         if (err < 0)
642         {
643           sprintf(PQerrormsg,
644                   "FATAL: StreamServerPort: bind() failed: errno=%d\n",
645                   errno);
646           pqdebug("%s", PQerrormsg);
647           strcat(PQerrormsg, "\tIs another postmaster already running on that port?\n");
648           if (family == AF_UNIX)
649             strcat(PQerrormsg, "\tIf not, remove socket node (/tmp/.s.PGSQL.<portnr>)and retry.\n");
650           else
651             strcat(PQerrormsg, "\tIf not, wait a few seconds and retry.\n");
652           fputs(PQerrormsg, stderr);
653           return (STATUS_ERROR);
654         }
655
656         listen(fd, SOMAXCONN);
657
658         /*
659          * MS: I took this code from Dillon's version.  It makes the listening
660          * port non-blocking.  That is not necessary (and may tickle kernel
661          * bugs).
662          *
663          * fcntl(fd, F_SETFD, 1); fcntl(fd, F_SETFL, FNDELAY);
664          */
665
666         *fdP = fd;
667         if (family == AF_UNIX)
668           {
669             chmod(sock_path, 0777);
670             atexit(do_unlink);
671           }
672         return (STATUS_OK);
673 }
674
675 /*
676  * StreamConnection -- create a new connection with client using
677  *              server port.
678  *
679  * This one should be non-blocking.
680  *
681  * RETURNS: STATUS_OK or STATUS_ERROR
682  */
683 int
684 StreamConnection(int server_fd, Port *port)
685 {
686         int                     len,
687                                 addrlen;
688         int                     family = port->raddr.in.sin_family;
689
690         /* accept connection (and fill in the client (remote) address) */
691         len = family == AF_INET ?
692                 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_un);
693         addrlen = len;
694         if ((port->sock = accept(server_fd,
695                                                          (struct sockaddr *) & port->raddr,
696                                                          &addrlen)) < 0)
697         {
698                 elog(ERROR, "postmaster: StreamConnection: accept: %m");
699                 return (STATUS_ERROR);
700         }
701
702         /* fill in the server (local) address */
703         addrlen = len;
704         if (getsockname(port->sock, (struct sockaddr *) & port->laddr,
705                                         &addrlen) < 0)
706         {
707                 elog(ERROR, "postmaster: StreamConnection: getsockname: %m");
708                 return (STATUS_ERROR);
709         }
710         if (family == AF_INET)
711         {
712                 struct protoent *pe;
713                 int                     on = 1;
714
715                 pe = getprotobyname("TCP");
716                 if (pe == NULL)
717                 {
718                         elog(ERROR, "postmaster: getprotobyname failed");
719                         return (STATUS_ERROR);
720                 }
721                 if (setsockopt(port->sock, pe->p_proto, TCP_NODELAY,
722                                            &on, sizeof(on)) < 0)
723                 {
724                         elog(ERROR, "postmaster: setsockopt failed");
725                         return (STATUS_ERROR);
726                 }
727         }
728
729         port->mask = 1 << port->sock;
730
731         /* reset to non-blocking */
732         fcntl(port->sock, F_SETFL, 1);
733
734         return (STATUS_OK);
735 }
736
737 /*
738  * StreamClose -- close a client/backend connection
739  */
740 void
741 StreamClose(int sock)
742 {
743         close(sock);
744 }
745
746 /* ---------------------------
747  * StreamOpen -- From client, initiate a connection with the
748  *              server (Postmaster).
749  *
750  * RETURNS: STATUS_OK or STATUS_ERROR
751  *
752  * NOTE: connection is NOT established just because this
753  *              routine exits.  Local state is ok, but we haven't
754  *              spoken to the postmaster yet.
755  * ---------------------------
756  */
757 int
758 StreamOpen(char *hostName, short portName, Port *port)
759 {
760         int                     len,
761                                 err;
762         struct hostent *hp;
763         extern int      errno;
764
765         /* set up the server (remote) address */
766         MemSet((char *) &port->raddr, 0, sizeof(port->raddr));
767         if (hostName)
768         {
769                 if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET)
770                 {
771                         sprintf(PQerrormsg,
772                                         "FATAL: StreamOpen: unknown hostname: %s\n",
773                                         hostName);
774                         fputs(PQerrormsg, stderr);
775                         pqdebug("%s", PQerrormsg);
776                         return (STATUS_ERROR);
777                 }
778                 memmove((char *) &(port->raddr.in.sin_addr),
779                                 (char *) hp->h_addr,
780                                 hp->h_length);
781                 port->raddr.in.sin_family = AF_INET;
782                 port->raddr.in.sin_port = htons(portName);
783                 len = sizeof(struct sockaddr_in);
784         }
785         else
786         {
787                 port->raddr.un.sun_family = AF_UNIX;
788                 len = UNIXSOCK_PATH(port->raddr.un, portName);
789         }
790         /* connect to the server */
791         if ((port->sock = socket(port->raddr.in.sin_family, SOCK_STREAM, 0)) < 0)
792         {
793                 sprintf(PQerrormsg,
794                                 "FATAL: StreamOpen: socket() failed: errno=%d\n",
795                                 errno);
796                 fputs(PQerrormsg, stderr);
797                 pqdebug("%s", PQerrormsg);
798                 return (STATUS_ERROR);
799         }
800         err = connect(port->sock, (struct sockaddr *) & port->raddr, len);
801         if (err < 0)
802         {
803                 sprintf(PQerrormsg,
804                                 "FATAL: StreamOpen: connect() failed: errno=%d\n",
805                                 errno);
806                 fputs(PQerrormsg, stderr);
807                 pqdebug("%s", PQerrormsg);
808                 return (STATUS_ERROR);
809         }
810
811         /* fill in the client address */
812         if (getsockname(port->sock, (struct sockaddr *) & port->laddr,
813                                         &len) < 0)
814         {
815                 sprintf(PQerrormsg,
816                                 "FATAL: StreamOpen: getsockname() failed: errno=%d\n",
817                                 errno);
818                 fputs(PQerrormsg, stderr);
819                 pqdebug("%s", PQerrormsg);
820                 return (STATUS_ERROR);
821         }
822
823         return (STATUS_OK);
824 }
825
826 static char *authentication_type_name[] = {
827         0, 0, 0, 0, 0, 0, 0,
828         "the default authentication type",
829         0, 0,
830         "Kerberos v4",
831         "Kerberos v5",
832         "host-based authentication",
833         "unauthenication",
834         "plaintext password authentication"
835 };
836
837 char       *
838 name_of_authentication_type(int type)
839 {
840         char       *result = 0;
841
842         if (type >= 1 && type <= LAST_AUTHENTICATION_TYPE)
843         {
844                 result = authentication_type_name[type];
845         }
846
847         if (result == 0)
848         {
849                 result = "<unknown authentication type>";
850         }
851
852         return result;
853 }