+
+static
+int get_syscall_nr(const char *syscall_name)
+{
+ int syscall_nr = -1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sc_table); i++) {
+ const struct trace_syscall_entry *entry;
+ const char *it_name;
+
+ entry = &sc_table[i];
+ if (!entry->desc)
+ continue;
+ it_name = entry->desc->name;
+ it_name += strlen(SYSCALL_ENTRY_STR);
+ if (!strcmp(syscall_name, it_name)) {
+ syscall_nr = i;
+ break;
+ }
+ }
+ return syscall_nr;
+}
+
+static
+int get_compat_syscall_nr(const char *syscall_name)
+{
+ int syscall_nr = -1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(compat_sc_table); i++) {
+ const struct trace_syscall_entry *entry;
+ const char *it_name;
+
+ entry = &compat_sc_table[i];
+ if (!entry->desc)
+ continue;
+ it_name = entry->desc->name;
+ it_name += strlen(COMPAT_SYSCALL_ENTRY_STR);
+ if (!strcmp(syscall_name, it_name)) {
+ syscall_nr = i;
+ break;
+ }
+ }
+ return syscall_nr;
+}
+
+int lttng_syscall_filter_enable(struct lttng_channel *chan,
+ const char *name)
+{
+ int syscall_nr, compat_syscall_nr, ret;
+ struct lttng_syscall_filter *filter;
+
+ WARN_ON_ONCE(!chan->sc_table);
+
+ if (!name) {
+ /* Enable all system calls by removing filter */
+ if (chan->sc_filter) {
+ filter = chan->sc_filter;
+ rcu_assign_pointer(chan->sc_filter, NULL);
+ synchronize_trace();
+ kfree(filter);
+ }
+ chan->syscall_all = 1;
+ return 0;
+ }
+
+ if (!chan->sc_filter) {
+ if (chan->syscall_all) {
+ /*
+ * All syscalls are already enabled.
+ */
+ return -EEXIST;
+ }
+ filter = kzalloc(sizeof(struct lttng_syscall_filter),
+ GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+ } else {
+ filter = chan->sc_filter;
+ }
+ syscall_nr = get_syscall_nr(name);
+ compat_syscall_nr = get_compat_syscall_nr(name);
+ if (syscall_nr < 0 && compat_syscall_nr < 0) {
+ ret = -ENOENT;
+ goto error;
+ }
+ if (syscall_nr >= 0) {
+ if (test_bit(syscall_nr, filter->sc)) {
+ ret = -EEXIST;
+ goto error;
+ }
+ bitmap_set(filter->sc, syscall_nr, 1);
+ }
+ if (compat_syscall_nr >= 0) {
+ if (test_bit(compat_syscall_nr, filter->sc_compat)) {
+ ret = -EEXIST;
+ goto error;
+ }
+ bitmap_set(filter->sc_compat, compat_syscall_nr, 1);
+ }
+ if (!chan->sc_filter)
+ rcu_assign_pointer(chan->sc_filter, filter);
+ return 0;
+
+error:
+ if (!chan->sc_filter)
+ kfree(filter);
+ return ret;
+}
+
+int lttng_syscall_filter_disable(struct lttng_channel *chan,
+ const char *name)
+{
+ int syscall_nr, compat_syscall_nr, ret;
+ struct lttng_syscall_filter *filter;
+
+ WARN_ON_ONCE(!chan->sc_table);
+
+ if (!chan->sc_filter) {
+ filter = kzalloc(sizeof(struct lttng_syscall_filter),
+ GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+ /* Trace all system calls, then apply disable. */
+ bitmap_set(filter->sc, 0, NR_syscalls);
+ bitmap_set(filter->sc_compat, 0, NR_compat_syscalls);
+ } else {
+ filter = chan->sc_filter;
+ }
+
+ if (!name) {
+ /* Disable all system calls */
+ bitmap_clear(filter->sc, 0, NR_syscalls);
+ bitmap_clear(filter->sc_compat, 0, NR_compat_syscalls);
+ goto apply_filter;
+ }
+ syscall_nr = get_syscall_nr(name);
+ compat_syscall_nr = get_compat_syscall_nr(name);
+ if (syscall_nr < 0 && compat_syscall_nr < 0) {
+ ret = -ENOENT;
+ goto error;
+ }
+ if (syscall_nr >= 0) {
+ if (!test_bit(syscall_nr, chan->sc_filter->sc)) {
+ ret = -EEXIST;
+ goto error;
+ }
+ bitmap_clear(chan->sc_filter->sc, syscall_nr, 1);
+ }
+ if (compat_syscall_nr >= 0) {
+ if (!test_bit(compat_syscall_nr, chan->sc_filter->sc_compat)) {
+ ret = -EEXIST;
+ goto error;
+ }
+ bitmap_clear(chan->sc_filter->sc_compat, compat_syscall_nr, 1);
+ }
+apply_filter:
+ if (!chan->sc_filter)
+ rcu_assign_pointer(chan->sc_filter, filter);
+ chan->syscall_all = 0;
+ return 0;
+
+error:
+ if (!chan->sc_filter)
+ kfree(filter);
+ return ret;
+}
+
+static
+const struct trace_syscall_entry *syscall_list_get_entry(loff_t *pos)
+{
+ const struct trace_syscall_entry *entry;
+ int iter = 0;
+
+ for (entry = sc_table;
+ entry < sc_table + ARRAY_SIZE(sc_table);
+ entry++) {
+ if (iter++ >= *pos)
+ return entry;
+ }
+ for (entry = compat_sc_table;
+ entry < compat_sc_table + ARRAY_SIZE(compat_sc_table);
+ entry++) {
+ if (iter++ >= *pos)
+ return entry;
+ }
+ /* End of list */
+ return NULL;
+}
+
+static
+void *syscall_list_start(struct seq_file *m, loff_t *pos)
+{
+ return (void *) syscall_list_get_entry(pos);
+}
+
+static
+void *syscall_list_next(struct seq_file *m, void *p, loff_t *ppos)
+{
+ (*ppos)++;
+ return (void *) syscall_list_get_entry(ppos);
+}
+
+static
+void syscall_list_stop(struct seq_file *m, void *p)
+{
+}
+
+static
+int syscall_list_show(struct seq_file *m, void *p)
+{
+ const struct trace_syscall_entry *table, *entry = p;
+ unsigned int bitness;
+
+ if (entry >= sc_table && entry < sc_table + ARRAY_SIZE(sc_table)) {
+ bitness = BITS_PER_LONG;
+ table = sc_table;
+ } else {
+ bitness = 32;
+ table = compat_sc_table;
+ WARN_ON_ONCE(!(entry >= compat_sc_table
+ && entry < compat_sc_table + ARRAY_SIZE(compat_sc_table)));
+ }
+ seq_printf(m, "syscall { name = %s; bitness = %u; };\n",
+ entry->desc->name,
+ bitness);
+ return 0;
+}
+
+static
+const struct seq_operations lttng_syscall_list_seq_ops = {
+ .start = syscall_list_start,
+ .next = syscall_list_next,
+ .stop = syscall_list_stop,
+ .show = syscall_list_show,
+};
+
+static
+int lttng_syscall_list_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, <tng_syscall_list_seq_ops);
+}
+
+const struct file_operations lttng_syscall_list_fops = {
+ .owner = THIS_MODULE,
+ .open = lttng_syscall_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};