Mewarnai input pengguna sulit karena dalam setengah kasus, itu adalah output oleh driver terminal (dengan gema lokal) sehingga dalam kasus itu, tidak ada aplikasi yang berjalan di terminal yang mungkin tahu kapan pengguna akan mengetik teks dan mengubah warna output sesuai . Hanya driver pseudo-terminal (di kernel) yang tahu (emulator terminal (seperti xterm) mengirimkannya beberapa karakter pada beberapa penekanan tombol dan driver terminal dapat mengirim kembali beberapa karakter untuk gema, tetapi xterm tidak dapat mengetahui apakah itu berasal dari gema lokal atau dari apa output aplikasi ke sisi budak dari terminal semu).
Dan kemudian, ada mode lain di mana driver terminal diberitahu untuk tidak menggema, tetapi aplikasi kali ini mengeluarkan sesuatu. Aplikasi (seperti yang menggunakan readline seperti gdb, bash ...) dapat mengirimkannya pada stdout atau stderr yang akan sulit dibedakan dari sesuatu yang di-output untuk hal-hal lain selain menggemakan kembali input pengguna.
Kemudian untuk membedakan stdout aplikasi dari stderr, ada beberapa pendekatan.
Banyak dari mereka melibatkan mengarahkan perintah stdout dan stderr ke pipa dan pipa-pipa itu dibaca oleh aplikasi untuk mewarnainya. Ada dua masalah dengan itu:
- Setelah stdout bukan lagi terminal (seperti pipa), banyak aplikasi cenderung menyesuaikan perilaku mereka untuk mulai buffering output mereka yang berarti bahwa output akan ditampilkan dalam potongan besar.
- Bahkan jika itu adalah proses yang sama yang memproses dua pipa, tidak ada jaminan bahwa urutan teks yang ditulis oleh aplikasi pada stdout dan stderr akan dipertahankan, karena proses membaca tidak dapat mengetahui (jika ada sesuatu yang bisa dibaca dari keduanya) apakah akan mulai membaca dari pipa "stdout" atau pipa "stderr".
Pendekatan lain adalah memodifikasi aplikasi sehingga warna stdout dan stdin. Seringkali tidak mungkin atau realistis untuk dilakukan.
Maka trik (untuk aplikasi yang terhubung secara dinamis) dapat dengan membajak (menggunakan $LD_PRELOADseperti dalam jawaban sickill ) fungsi keluaran yang disebut oleh aplikasi untuk menampilkan sesuatu dan memasukkan kode di dalamnya yang menetapkan warna latar depan berdasarkan apakah mereka dimaksudkan untuk menghasilkan sesuatu pada stderr atau stdout. Namun, itu berarti membajak setiap fungsi yang mungkin dari pustaka C dan pustaka lain yang melakukan write(2)syscall secara langsung dipanggil oleh aplikasi yang berpotensi berakhir menulis sesuatu di stdout atau stderr (printf, put, perror ...), dan bahkan kemudian , yang dapat mengubah perilakunya.
Pendekatan lain bisa menggunakan trik PTRACE seperti straceatau gdblakukan untuk mengaitkan diri kita sendiri setiap kali write(2)panggilan sistem dipanggil dan mengatur warna output berdasarkan pada apakah write(2)ada pada file deskriptor 1 atau 2.
Namun, itu hal yang cukup besar untuk dilakukan.
Trik yang baru saja saya mainkan adalah dengan membajak stracedirinya sendiri (yang melakukan pekerjaan kotor dengan mengaitkan dirinya sendiri sebelum setiap panggilan sistem) menggunakan LD_PRELOAD, untuk mengatakannya untuk mengubah warna output berdasarkan apakah ia telah mendeteksi write(2)pada fd 1 atau 2.
Dari melihat stracekode sumber, kita dapat melihat bahwa semua outputnya dilakukan melalui vfprintffungsi. Yang perlu kita lakukan adalah membajak fungsi itu.
Pembungkus LD_PRELOAD akan terlihat seperti:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
static int c = 0;
va_list ap_orig;
va_copy(ap_orig, ap);
if (!orig_vfprintf) {
orig_vfprintf = (int (*) (FILE*, const char *, va_list))
dlsym (RTLD_NEXT, "vfprintf");
}
if (strcmp(fmt, "%ld, ") == 0) {
int fd = va_arg(ap, long);
switch (fd) {
case 2:
write(2, "\e[31m", 5);
c = 1;
break;
case 1:
write(2, "\e[32m", 5);
c = 1;
break;
}
} else if (strcmp(fmt, ") ") == 0) {
if (c) write(2, "\e[m", 3);
c = 0;
}
return orig_vfprintf(outf, fmt, ap_orig);
}
Lalu, kami kompilasi dengan:
cc -Wall -fpic -shared -o wrap.so wrap.c -ldl
Dan gunakan sebagai:
LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd
Anda akan melihat bagaimana jika Anda menggantinya some-cmddengan bash, bash prompt dan apa yang Anda ketik muncul dengan warna merah (stderr) sementara dengan zshitu muncul dalam warna hitam (karena zsh dups stderr ke fd baru untuk menampilkan prompt dan gema).
Tampaknya bekerja dengan sangat baik bahkan untuk aplikasi yang tidak Anda harapkan (seperti yang menggunakan warna).
Mode pewarnaan adalah keluaran pada stracestderr yang dianggap terminal. Jika aplikasi mengarahkan ulang stdout atau stderr, strace kami yang dibajak akan terus menulis urutan pewarnaan escape pada terminal.
Solusi itu memiliki keterbatasan:
- Yang melekat pada
strace: masalah kinerja, Anda tidak dapat menjalankan perintah PTRACE lainnya seperti straceatau gdbdi dalamnya, atau masalah setuid / setgid
- Ini mewarnai berdasarkan
writepada stdout / stderr dari setiap proses individu. Jadi misalnya, in sh -c 'echo error >&2', errorakan berwarna hijau karena echomenampilkannya pada stdout -nya (yang harus diarahkan ke sh's stderr, tetapi semua strace saw adalah a write(1, "error\n", 6)). Dan dalam sh -c 'seq 1000000 | wc', seqtidak banyak atau writes untuk yang stdout, sehingga bungkusnya akan berakhir outputing banyak (tak terlihat) melarikan diri urutan ke terminal.