]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2001, 2002 Jeff Dike ([email protected]) | |
3 | * Licensed under the GPL | |
4 | */ | |
5 | ||
6 | #include "linux/sched.h" | |
7 | #include "linux/slab.h" | |
8 | #include "linux/list.h" | |
9 | #include "linux/kd.h" | |
10 | #include "linux/interrupt.h" | |
11 | #include "linux/devfs_fs_kernel.h" | |
12 | #include "asm/uaccess.h" | |
13 | #include "chan_kern.h" | |
14 | #include "irq_user.h" | |
15 | #include "line.h" | |
16 | #include "kern.h" | |
17 | #include "user_util.h" | |
18 | #include "kern_util.h" | |
19 | #include "os.h" | |
20 | #include "irq_kern.h" | |
21 | ||
22 | #define LINE_BUFSIZE 4096 | |
23 | ||
24 | static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) | |
25 | { | |
26 | struct tty_struct *tty = data; | |
27 | struct line *line = tty->driver_data; | |
28 | ||
29 | if (line) | |
30 | chan_interrupt(&line->chan_list, &line->task, tty, irq); | |
31 | return IRQ_HANDLED; | |
32 | } | |
33 | ||
34 | static void line_timer_cb(void *arg) | |
35 | { | |
36 | struct tty_struct *tty = arg; | |
37 | struct line *line = tty->driver_data; | |
38 | ||
39 | line_interrupt(line->driver->read_irq, arg, NULL); | |
40 | } | |
41 | ||
42 | static int write_room(struct line *dev) | |
43 | { | |
44 | int n; | |
45 | ||
46 | if (dev->buffer == NULL) | |
47 | return (LINE_BUFSIZE - 1); | |
48 | ||
49 | n = dev->head - dev->tail; | |
50 | if (n <= 0) | |
51 | n = LINE_BUFSIZE + n; | |
52 | return (n - 1); | |
53 | } | |
54 | ||
55 | static int buffer_data(struct line *line, const char *buf, int len) | |
56 | { | |
57 | int end, room; | |
58 | ||
59 | if(line->buffer == NULL){ | |
60 | line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC); | |
61 | if (line->buffer == NULL) { | |
62 | printk("buffer_data - atomic allocation failed\n"); | |
63 | return(0); | |
64 | } | |
65 | line->head = line->buffer; | |
66 | line->tail = line->buffer; | |
67 | } | |
68 | ||
69 | room = write_room(line); | |
70 | len = (len > room) ? room : len; | |
71 | ||
72 | end = line->buffer + LINE_BUFSIZE - line->tail; | |
73 | if(len < end){ | |
74 | memcpy(line->tail, buf, len); | |
75 | line->tail += len; | |
76 | } | |
77 | else { | |
78 | memcpy(line->tail, buf, end); | |
79 | buf += end; | |
80 | memcpy(line->buffer, buf, len - end); | |
81 | line->tail = line->buffer + len - end; | |
82 | } | |
83 | ||
84 | return(len); | |
85 | } | |
86 | ||
87 | static int flush_buffer(struct line *line) | |
88 | { | |
89 | int n, count; | |
90 | ||
91 | if ((line->buffer == NULL) || (line->head == line->tail)) | |
92 | return(1); | |
93 | ||
94 | if (line->tail < line->head) { | |
95 | count = line->buffer + LINE_BUFSIZE - line->head; | |
96 | n = write_chan(&line->chan_list, line->head, count, | |
97 | line->driver->write_irq); | |
98 | if (n < 0) | |
99 | return(n); | |
100 | if (n == count) | |
101 | line->head = line->buffer; | |
102 | else { | |
103 | line->head += n; | |
104 | return(0); | |
105 | } | |
106 | } | |
107 | ||
108 | count = line->tail - line->head; | |
109 | n = write_chan(&line->chan_list, line->head, count, | |
110 | line->driver->write_irq); | |
111 | if(n < 0) return(n); | |
112 | ||
113 | line->head += n; | |
114 | return(line->head == line->tail); | |
115 | } | |
116 | ||
117 | int line_write(struct tty_struct *tty, const unsigned char *buf, int len) | |
118 | { | |
119 | struct line *line = tty->driver_data; | |
120 | unsigned long flags; | |
121 | int n, err, ret = 0; | |
122 | ||
123 | if(tty->stopped) return 0; | |
124 | ||
125 | down(&line->sem); | |
126 | if(line->head != line->tail){ | |
127 | local_irq_save(flags); | |
128 | ret = buffer_data(line, buf, len); | |
129 | err = flush_buffer(line); | |
130 | local_irq_restore(flags); | |
131 | if(err <= 0 && (err != -EAGAIN || !ret)) | |
132 | ret = err; | |
133 | } | |
134 | else { | |
135 | n = write_chan(&line->chan_list, buf, len, | |
136 | line->driver->write_irq); | |
137 | if(n < 0){ | |
138 | ret = n; | |
139 | goto out_up; | |
140 | } | |
141 | ||
142 | len -= n; | |
143 | ret += n; | |
144 | if(len > 0) | |
145 | ret += buffer_data(line, buf + n, len); | |
146 | } | |
147 | out_up: | |
148 | up(&line->sem); | |
149 | return(ret); | |
150 | } | |
151 | ||
152 | void line_put_char(struct tty_struct *tty, unsigned char ch) | |
153 | { | |
154 | line_write(tty, &ch, sizeof(ch)); | |
155 | } | |
156 | ||
157 | void line_set_termios(struct tty_struct *tty, struct termios * old) | |
158 | { | |
159 | /* nothing */ | |
160 | } | |
161 | ||
162 | int line_chars_in_buffer(struct tty_struct *tty) | |
163 | { | |
164 | return 0; | |
165 | } | |
166 | ||
167 | static struct { | |
168 | int cmd; | |
169 | char *level; | |
170 | char *name; | |
171 | } tty_ioctls[] = { | |
172 | /* don't print these, they flood the log ... */ | |
173 | { TCGETS, NULL, "TCGETS" }, | |
174 | { TCSETS, NULL, "TCSETS" }, | |
175 | { TCSETSW, NULL, "TCSETSW" }, | |
176 | { TCFLSH, NULL, "TCFLSH" }, | |
177 | { TCSBRK, NULL, "TCSBRK" }, | |
178 | ||
179 | /* general tty stuff */ | |
180 | { TCSETSF, KERN_DEBUG, "TCSETSF" }, | |
181 | { TCGETA, KERN_DEBUG, "TCGETA" }, | |
182 | { TIOCMGET, KERN_DEBUG, "TIOCMGET" }, | |
183 | { TCSBRKP, KERN_DEBUG, "TCSBRKP" }, | |
184 | { TIOCMSET, KERN_DEBUG, "TIOCMSET" }, | |
185 | ||
186 | /* linux-specific ones */ | |
187 | { TIOCLINUX, KERN_INFO, "TIOCLINUX" }, | |
188 | { KDGKBMODE, KERN_INFO, "KDGKBMODE" }, | |
189 | { KDGKBTYPE, KERN_INFO, "KDGKBTYPE" }, | |
190 | { KDSIGACCEPT, KERN_INFO, "KDSIGACCEPT" }, | |
191 | }; | |
192 | ||
193 | int line_ioctl(struct tty_struct *tty, struct file * file, | |
194 | unsigned int cmd, unsigned long arg) | |
195 | { | |
196 | int ret; | |
197 | int i; | |
198 | ||
199 | ret = 0; | |
200 | switch(cmd) { | |
201 | #ifdef TIOCGETP | |
202 | case TIOCGETP: | |
203 | case TIOCSETP: | |
204 | case TIOCSETN: | |
205 | #endif | |
206 | #ifdef TIOCGETC | |
207 | case TIOCGETC: | |
208 | case TIOCSETC: | |
209 | #endif | |
210 | #ifdef TIOCGLTC | |
211 | case TIOCGLTC: | |
212 | case TIOCSLTC: | |
213 | #endif | |
214 | case TCGETS: | |
215 | case TCSETSF: | |
216 | case TCSETSW: | |
217 | case TCSETS: | |
218 | case TCGETA: | |
219 | case TCSETAF: | |
220 | case TCSETAW: | |
221 | case TCSETA: | |
222 | case TCXONC: | |
223 | case TCFLSH: | |
224 | case TIOCOUTQ: | |
225 | case TIOCINQ: | |
226 | case TIOCGLCKTRMIOS: | |
227 | case TIOCSLCKTRMIOS: | |
228 | case TIOCPKT: | |
229 | case TIOCGSOFTCAR: | |
230 | case TIOCSSOFTCAR: | |
231 | return -ENOIOCTLCMD; | |
232 | #if 0 | |
233 | case TCwhatever: | |
234 | /* do something */ | |
235 | break; | |
236 | #endif | |
237 | default: | |
238 | for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++) | |
239 | if (cmd == tty_ioctls[i].cmd) | |
240 | break; | |
241 | if (i < ARRAY_SIZE(tty_ioctls)) { | |
242 | if (NULL != tty_ioctls[i].level) | |
243 | printk("%s%s: %s: ioctl %s called\n", | |
244 | tty_ioctls[i].level, __FUNCTION__, | |
245 | tty->name, tty_ioctls[i].name); | |
246 | } else { | |
247 | printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n", | |
248 | __FUNCTION__, tty->name, cmd); | |
249 | } | |
250 | ret = -ENOIOCTLCMD; | |
251 | break; | |
252 | } | |
253 | return(ret); | |
254 | } | |
255 | ||
256 | static irqreturn_t line_write_interrupt(int irq, void *data, | |
257 | struct pt_regs *unused) | |
258 | { | |
259 | struct tty_struct *tty = data; | |
260 | struct line *line = tty->driver_data; | |
261 | int err; | |
262 | ||
263 | err = flush_buffer(line); | |
264 | if(err == 0) | |
265 | return(IRQ_NONE); | |
266 | else if(err < 0){ | |
267 | line->head = line->buffer; | |
268 | line->tail = line->buffer; | |
269 | } | |
270 | ||
271 | if(tty == NULL) | |
272 | return(IRQ_NONE); | |
273 | ||
274 | if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && | |
275 | (tty->ldisc.write_wakeup != NULL)) | |
276 | (tty->ldisc.write_wakeup)(tty); | |
277 | ||
278 | /* BLOCKING mode | |
279 | * In blocking mode, everything sleeps on tty->write_wait. | |
280 | * Sleeping in the console driver would break non-blocking | |
281 | * writes. | |
282 | */ | |
283 | ||
284 | if(waitqueue_active(&tty->write_wait)) | |
285 | wake_up_interruptible(&tty->write_wait); | |
286 | return(IRQ_HANDLED); | |
287 | } | |
288 | ||
289 | int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) | |
290 | { | |
291 | struct line *line = tty->driver_data; | |
292 | struct line_driver *driver = line->driver; | |
293 | int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM; | |
294 | ||
295 | if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, | |
296 | line_interrupt, flags, | |
297 | driver->read_irq_name, tty); | |
298 | if(err) return(err); | |
299 | if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, | |
300 | line_write_interrupt, flags, | |
301 | driver->write_irq_name, tty); | |
302 | line->have_irq = 1; | |
303 | return(err); | |
304 | } | |
305 | ||
306 | void line_disable(struct tty_struct *tty, int current_irq) | |
307 | { | |
308 | struct line *line = tty->driver_data; | |
309 | ||
310 | if(!line->have_irq) | |
311 | return; | |
312 | ||
313 | if(line->driver->read_irq == current_irq) | |
314 | free_irq_later(line->driver->read_irq, tty); | |
315 | else { | |
316 | free_irq_by_irq_and_dev(line->driver->read_irq, tty); | |
317 | free_irq(line->driver->read_irq, tty); | |
318 | } | |
319 | ||
320 | if(line->driver->write_irq == current_irq) | |
321 | free_irq_later(line->driver->write_irq, tty); | |
322 | else { | |
323 | free_irq_by_irq_and_dev(line->driver->write_irq, tty); | |
324 | free_irq(line->driver->write_irq, tty); | |
325 | } | |
326 | ||
327 | line->have_irq = 0; | |
328 | } | |
329 | ||
330 | int line_open(struct line *lines, struct tty_struct *tty, | |
331 | struct chan_opts *opts) | |
332 | { | |
333 | struct line *line; | |
334 | int err = 0; | |
335 | ||
336 | line = &lines[tty->index]; | |
337 | tty->driver_data = line; | |
338 | ||
339 | down(&line->sem); | |
340 | if (tty->count == 1) { | |
341 | if (!line->valid) { | |
342 | err = -ENODEV; | |
343 | goto out; | |
344 | } | |
345 | if (list_empty(&line->chan_list)) { | |
346 | err = parse_chan_pair(line->init_str, &line->chan_list, | |
347 | line->init_pri, tty->index, opts); | |
348 | if(err) goto out; | |
349 | err = open_chan(&line->chan_list); | |
350 | if(err) goto out; | |
351 | } | |
352 | enable_chan(&line->chan_list, tty); | |
353 | INIT_WORK(&line->task, line_timer_cb, tty); | |
354 | } | |
355 | ||
356 | if(!line->sigio){ | |
357 | chan_enable_winch(&line->chan_list, tty); | |
358 | line->sigio = 1; | |
359 | } | |
360 | chan_window_size(&line->chan_list, &tty->winsize.ws_row, | |
361 | &tty->winsize.ws_col); | |
362 | line->count++; | |
363 | ||
364 | out: | |
365 | up(&line->sem); | |
366 | return(err); | |
367 | } | |
368 | ||
369 | void line_close(struct tty_struct *tty, struct file * filp) | |
370 | { | |
371 | struct line *line = tty->driver_data; | |
372 | ||
373 | down(&line->sem); | |
374 | line->count--; | |
375 | if (tty->count == 1) { | |
376 | line_disable(tty, -1); | |
377 | tty->driver_data = NULL; | |
378 | } | |
379 | up(&line->sem); | |
380 | } | |
381 | ||
382 | void close_lines(struct line *lines, int nlines) | |
383 | { | |
384 | int i; | |
385 | ||
386 | for(i = 0; i < nlines; i++) | |
387 | close_chan(&lines[i].chan_list); | |
388 | } | |
389 | ||
390 | int line_setup(struct line *lines, int num, char *init, int all_allowed) | |
391 | { | |
392 | int i, n; | |
393 | char *end; | |
394 | ||
395 | if(*init == '=') n = -1; | |
396 | else { | |
397 | n = simple_strtoul(init, &end, 0); | |
398 | if(*end != '='){ | |
399 | printk(KERN_ERR "line_setup failed to parse \"%s\"\n", | |
400 | init); | |
401 | return(0); | |
402 | } | |
403 | init = end; | |
404 | } | |
405 | init++; | |
406 | if((n >= 0) && (n >= num)){ | |
407 | printk("line_setup - %d out of range ((0 ... %d) allowed)\n", | |
408 | n, num - 1); | |
409 | return(0); | |
410 | } | |
411 | else if (n >= 0){ | |
412 | if (lines[n].count > 0) { | |
413 | printk("line_setup - device %d is open\n", n); | |
414 | return(0); | |
415 | } | |
416 | if (lines[n].init_pri <= INIT_ONE){ | |
417 | lines[n].init_pri = INIT_ONE; | |
418 | if (!strcmp(init, "none")) | |
419 | lines[n].valid = 0; | |
420 | else { | |
421 | lines[n].init_str = init; | |
422 | lines[n].valid = 1; | |
423 | } | |
424 | } | |
425 | } | |
426 | else if(!all_allowed){ | |
427 | printk("line_setup - can't configure all devices from " | |
428 | "mconsole\n"); | |
429 | return(0); | |
430 | } | |
431 | else { | |
432 | for(i = 0; i < num; i++){ | |
433 | if(lines[i].init_pri <= INIT_ALL){ | |
434 | lines[i].init_pri = INIT_ALL; | |
435 | if(!strcmp(init, "none")) lines[i].valid = 0; | |
436 | else { | |
437 | lines[i].init_str = init; | |
438 | lines[i].valid = 1; | |
439 | } | |
440 | } | |
441 | } | |
442 | } | |
443 | return(1); | |
444 | } | |
445 | ||
446 | int line_config(struct line *lines, int num, char *str) | |
447 | { | |
448 | char *new = uml_strdup(str); | |
449 | ||
450 | if(new == NULL){ | |
451 | printk("line_config - uml_strdup failed\n"); | |
452 | return(-ENOMEM); | |
453 | } | |
454 | return(!line_setup(lines, num, new, 0)); | |
455 | } | |
456 | ||
457 | int line_get_config(char *name, struct line *lines, int num, char *str, | |
458 | int size, char **error_out) | |
459 | { | |
460 | struct line *line; | |
461 | char *end; | |
462 | int dev, n = 0; | |
463 | ||
464 | dev = simple_strtoul(name, &end, 0); | |
465 | if((*end != '\0') || (end == name)){ | |
466 | *error_out = "line_get_config failed to parse device number"; | |
467 | return(0); | |
468 | } | |
469 | ||
470 | if((dev < 0) || (dev >= num)){ | |
471 | *error_out = "device number of of range"; | |
472 | return(0); | |
473 | } | |
474 | ||
475 | line = &lines[dev]; | |
476 | ||
477 | down(&line->sem); | |
478 | if(!line->valid) | |
479 | CONFIG_CHUNK(str, size, n, "none", 1); | |
480 | else if(line->count == 0) | |
481 | CONFIG_CHUNK(str, size, n, line->init_str, 1); | |
482 | else n = chan_config_string(&line->chan_list, str, size, error_out); | |
483 | up(&line->sem); | |
484 | ||
485 | return(n); | |
486 | } | |
487 | ||
488 | int line_remove(struct line *lines, int num, char *str) | |
489 | { | |
490 | char config[sizeof("conxxxx=none\0")]; | |
491 | ||
492 | sprintf(config, "%s=none", str); | |
493 | return(!line_setup(lines, num, config, 0)); | |
494 | } | |
495 | ||
496 | int line_write_room(struct tty_struct *tty) | |
497 | { | |
498 | struct line *dev = tty->driver_data; | |
499 | int room; | |
500 | ||
501 | if (tty->stopped) | |
502 | return 0; | |
503 | room = write_room(dev); | |
504 | if (0 == room) | |
505 | printk(KERN_DEBUG "%s: %s: no room left in buffer\n", | |
506 | __FUNCTION__,tty->name); | |
507 | return room; | |
508 | } | |
509 | ||
510 | struct tty_driver *line_register_devfs(struct lines *set, | |
511 | struct line_driver *line_driver, | |
512 | struct tty_operations *ops, struct line *lines, | |
513 | int nlines) | |
514 | { | |
515 | int i; | |
516 | struct tty_driver *driver = alloc_tty_driver(nlines); | |
517 | ||
518 | if (!driver) | |
519 | return NULL; | |
520 | ||
521 | driver->driver_name = line_driver->name; | |
522 | driver->name = line_driver->device_name; | |
523 | driver->devfs_name = line_driver->devfs_name; | |
524 | driver->major = line_driver->major; | |
525 | driver->minor_start = line_driver->minor_start; | |
526 | driver->type = line_driver->type; | |
527 | driver->subtype = line_driver->subtype; | |
528 | driver->flags = TTY_DRIVER_REAL_RAW; | |
529 | driver->init_termios = tty_std_termios; | |
530 | tty_set_operations(driver, ops); | |
531 | ||
532 | if (tty_register_driver(driver)) { | |
533 | printk("%s: can't register %s driver\n", | |
534 | __FUNCTION__,line_driver->name); | |
535 | put_tty_driver(driver); | |
536 | return NULL; | |
537 | } | |
538 | ||
539 | for(i = 0; i < nlines; i++){ | |
540 | if(!lines[i].valid) | |
541 | tty_unregister_device(driver, i); | |
542 | } | |
543 | ||
544 | mconsole_register_dev(&line_driver->mc); | |
545 | return driver; | |
546 | } | |
547 | ||
548 | void lines_init(struct line *lines, int nlines) | |
549 | { | |
550 | struct line *line; | |
551 | int i; | |
552 | ||
553 | for(i = 0; i < nlines; i++){ | |
554 | line = &lines[i]; | |
555 | INIT_LIST_HEAD(&line->chan_list); | |
556 | sema_init(&line->sem, 1); | |
557 | if(line->init_str != NULL){ | |
558 | line->init_str = uml_strdup(line->init_str); | |
559 | if(line->init_str == NULL) | |
560 | printk("lines_init - uml_strdup returned " | |
561 | "NULL\n"); | |
562 | } | |
563 | } | |
564 | } | |
565 | ||
566 | struct winch { | |
567 | struct list_head list; | |
568 | int fd; | |
569 | int tty_fd; | |
570 | int pid; | |
571 | struct tty_struct *tty; | |
572 | }; | |
573 | ||
574 | irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) | |
575 | { | |
576 | struct winch *winch = data; | |
577 | struct tty_struct *tty; | |
578 | struct line *line; | |
579 | int err; | |
580 | char c; | |
581 | ||
582 | if(winch->fd != -1){ | |
583 | err = generic_read(winch->fd, &c, NULL); | |
584 | if(err < 0){ | |
585 | if(err != -EAGAIN){ | |
586 | printk("winch_interrupt : read failed, " | |
587 | "errno = %d\n", -err); | |
588 | printk("fd %d is losing SIGWINCH support\n", | |
589 | winch->tty_fd); | |
590 | return(IRQ_HANDLED); | |
591 | } | |
592 | goto out; | |
593 | } | |
594 | } | |
595 | tty = winch->tty; | |
596 | if (tty != NULL) { | |
597 | line = tty->driver_data; | |
598 | chan_window_size(&line->chan_list, | |
599 | &tty->winsize.ws_row, | |
600 | &tty->winsize.ws_col); | |
601 | kill_pg(tty->pgrp, SIGWINCH, 1); | |
602 | } | |
603 | out: | |
604 | if(winch->fd != -1) | |
605 | reactivate_fd(winch->fd, WINCH_IRQ); | |
606 | return(IRQ_HANDLED); | |
607 | } | |
608 | ||
609 | DECLARE_MUTEX(winch_handler_sem); | |
610 | LIST_HEAD(winch_handlers); | |
611 | ||
612 | void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) | |
613 | { | |
614 | struct winch *winch; | |
615 | ||
616 | down(&winch_handler_sem); | |
617 | winch = kmalloc(sizeof(*winch), GFP_KERNEL); | |
618 | if (winch == NULL) { | |
619 | printk("register_winch_irq - kmalloc failed\n"); | |
620 | goto out; | |
621 | } | |
622 | *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), | |
623 | .fd = fd, | |
624 | .tty_fd = tty_fd, | |
625 | .pid = pid, | |
626 | .tty = tty }); | |
627 | list_add(&winch->list, &winch_handlers); | |
628 | if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, | |
629 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, | |
630 | "winch", winch) < 0) | |
631 | printk("register_winch_irq - failed to register IRQ\n"); | |
632 | out: | |
633 | up(&winch_handler_sem); | |
634 | } | |
635 | ||
636 | static void winch_cleanup(void) | |
637 | { | |
638 | struct list_head *ele; | |
639 | struct winch *winch; | |
640 | ||
641 | list_for_each(ele, &winch_handlers){ | |
642 | winch = list_entry(ele, struct winch, list); | |
643 | if(winch->fd != -1){ | |
644 | deactivate_fd(winch->fd, WINCH_IRQ); | |
645 | os_close_file(winch->fd); | |
646 | } | |
647 | if(winch->pid != -1) | |
648 | os_kill_process(winch->pid, 1); | |
649 | } | |
650 | } | |
651 | __uml_exitcall(winch_cleanup); | |
652 | ||
653 | char *add_xterm_umid(char *base) | |
654 | { | |
655 | char *umid, *title; | |
656 | int len; | |
657 | ||
658 | umid = get_umid(1); | |
659 | if(umid == NULL) return(base); | |
660 | ||
661 | len = strlen(base) + strlen(" ()") + strlen(umid) + 1; | |
662 | title = kmalloc(len, GFP_KERNEL); | |
663 | if(title == NULL){ | |
664 | printk("Failed to allocate buffer for xterm title\n"); | |
665 | return(base); | |
666 | } | |
667 | ||
668 | snprintf(title, len, "%s (%s)", base, umid); | |
669 | return(title); | |
670 | } | |
671 | ||
672 | /* | |
673 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
674 | * Emacs will notice this stuff at the end of the file and automatically | |
675 | * adjust the settings for this buffer only. This must remain at the end | |
676 | * of the file. | |
677 | * --------------------------------------------------------------------------- | |
678 | * Local variables: | |
679 | * c-file-style: "linux" | |
680 | * End: | |
681 | */ |