Combined stack size too large error while loading eBPF program - ebpf

I am trying to collect simple flow level statistics using eBPF program.
I define a map as follows:
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct network_tuple_xdp);
__type(value, struct flow_state_xdp);
__uint(max_entries, 64);
} flow_state_xdp_map SEC(".maps");
UPDATE: adding a new additional map solves the error.
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct network_tuple_xdp);
__type(value, struct flow_state_xdp_2);
__uint(max_entries, 64);
} flow_state_xdp_map_2 SEC(".maps");
The structures used in the above map are defined as follows:
struct network_tuple_xdp
{
__u32 saddr;
__u32 daddr;
__u32 sport;
__u32 dport;
};
struct flow_state_xdp
{
__u32 flow_initialized;
__u32 seq_num_curr;
__u32 seq_num_prev;
__u32 rto_count;
__u32 tsval;
__u32 tsecr;
__u32 flow_size;
__u32 fct;
};
UPDATE: Structure for the new map. If you use this structure, then comment out the last 4 members in the struct flow_state_xdp
struct flow_state_xdp_2
{
__u32 tsval;
__u32 tsecr;
__u32 flow_size;
__u32 fct;
};
I initialize the members of the structures in the following function:
static struct flow_state_xdp *
create_flow_state_xdp(struct xdp_md *ctx,
struct packet_info *p_info,
struct network_tuple_xdp *key)
{
struct flow_state_xdp flow_state = {
.seq_num_prev = 0,
.seq_num_curr = p_info->seq_num,
.flow_initialized = 0,
.rto_count = 0,
.tsval = 0,
.tsecr = 0,
.flow_size = 0,
.fct = 0,
};
if (bpf_map_update_elem(&flow_state_xdp_map, key, &flow_state,
BPF_NOEXIST) < 0)
{
send_map_full_event(ctx, p_info, XMAP_FLOWSTATE);
return NULL;
}
return bpf_map_lookup_elem(&flow_state_xdp_map, key);
}
static struct flow_state_xdp *
create_flow_state_xdp_2(struct xdp_md *ctx,
struct packet_info *p_info,
struct network_tuple_xdp *key)
{ // same as above function with updated names of variables accordingly}
UPDATE: How create_flow_state_xdp is called...
static void packet_event_xdp(struct xdp_md *ctx,
struct packet_info *p_info,
struct network_tuple_xdp *key)
{
struct flow_state_xdp *flow_stats =
bpf_map_lookup_elem(&flow_state_xdp_map, key);
// exclude this statement to reproduce the error
struct flow_state_xdp_2 *flow_stats2 =
bpf_map_lookup_elem(&flow_state_xdp_map_2, key);
if (flow_stats)
{ // some operations if the key (flow tuple) for the flow exists }
else // create new entry for flow
{
flow_stats = create_flow_state_xdp(ctx, p_info, key);
// exclude this statement to reproduce the error
flow_stats2 = create_flow_state_xdp_2(ctx, p_info, key);
}
}
static void prog_xdp(struct xdp_md *ctx)
{
struct packet_info p_info = { 0 };
struct network_tuple_xdp *key = get_flow_key_from_packet_xdp(ctx, &p_info);
packet_event_xdp(ctx, &p_info, key);
}
SEC("xdp")
int xdp_ingress(struct xdp_md *ctx)
{
prog_xdp(ctx);
return XDP_PASS;
}
However, the program is not loaded due to the following error of combined stack size too large as mentioned below:
libbpf: prog 'prog_xdp_ingress': BPF program load failed: Permission denied
libbpf: prog 'prog_xdp_ingress': -- BEGIN PROG LOAD LOG --
combined stack size of 2 calls is 544. Too large
processed 192213 insns (limit 1000000) max_states_per_insn 31 total_states 12609 peak_states 1056 mark_read 41
-- END PROG LOAD LOG --
This error does not happen when there are 4 members in struct flow_state_xdp (seq_num_prev, seq_num_curr, flow_initialized and rto_count)
Could some explain what causes the error of combined stack size? Considering limitation of stack size, I tried with BPF_MAP_TYPE_PERCPU_HASH too but get the same error. Also, I make sure that the alignment of the whole structure is a multiple of 8 bytes.
Any solution of how all the 8 members can be defined in the struct flow_state_xdp?

Related

```bpf_probe_read``` garbled text,how to get plaintext

I want to get getaddrinfo function entry params(host->PT_REGS_PARM1), attach uretprobe/getaddrinfo, but it return any garbled text, how to get plaintext?
using golang cilium/ebpf
the uretprobe.c
#include "common.h"
#include "bpf_helpers.h"
#include "bpf_tracing.h"
char __license[] SEC("license") = "Dual MIT/GPL";
struct event {
u32 pid;
u8 comm[16];
u8 host[80];
};
struct {
// __uint(type, BPF_MAP_TYPE_RINGBUF);
// __uint(max_entries, 256 * 1024 /* 256 KB */);
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
} events SEC(".maps");
struct event *unused __attribute__((unused));
SEC("uretprobe/getaddrinfo")
int getaddrinfo_return(struct pt_regs *ctx)
{
struct event event = {};
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid >> 32;
u32 tid = (u32)pid_tgid;
bpf_probe_read(&event.host, sizeof(event.host),
(void *)PT_REGS_PARM1(ctx));
bpf_get_current_comm(&event.comm, 16);
event.pid = pid;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
return 0;
}
the main.go and log print
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS --target=amd64 -type event bpf uretprobe.c -- -I../headers
binPath = "/lib/x86_64-linux-gnu/libc.so.6"
symbol = "getaddrinfo"
log.Printf("%s:%s return value:%d - %16s - %80s", binPath, symbol, event.Pid, event.Comm, event.Host,)
2022/09/18 08:47:24 /lib/x86_64-linux-gnu/libc.so.6:getaddrinfo return value:1460362 - curl - *P���qsʀv�Y��sqU\\�W�� sqU�a���]�W�U�Y�
Thank you in advance.

linux device driver - ADS1292R problem in reading chip id on spi

I am novice to Linux device driver and writing driver module for TI-ADS1292R chip. This driver is loading without any error, creates character device entry in /dev directory. I am also able to transfer data on SPI bus (verified it on logic analyzer and DSO).
There are two problems here.
(1) in logic analyzer it shows that chip is responding with correct chip id (0x73) on read id command, but in my driver I am getting incorrect value (0x80). Why this is happening and where the problem is?
(2) As far as I understood, my driver should automatically loaded if I have configured it through defconfig file, which is not happening in my case. Right now, I am loading it as a built-in module with obj-y option.
My code has following snippets through which I am trying to read the chip id.
static int ads_send_byte(struct ads1292_chip *dev, int data)
{
int status;
struct spi_message msg = { };
struct spi_transfer transfer = { };
dev->tx_buff[0] = data;
transfer.tx_buf = dev->tx_buff;
transfer.rx_buf = dev->rx_buff;
transfer.len = 1;
transfer.speed_hz = SPI_BUS_SPEED;
spi_message_init(&msg);
spi_message_add_tail(&transfer, &msg);
status = spi_sync(dev->spi, &msg);
printk(KERN_DEBUG "ecg: %s (%#x) %d\n", __func__, data, status);
return status;
};
static int ads_send_RREG(struct ads1292_chip *dev, char __user * buf, u8 reg, u8 size)
{
struct spi_message msg = { };
struct spi_transfer transfer = { };
int ret;
dev->tx_buff[0] = ADS1292_RREG | reg;
dev->tx_buff[1] = size - 1; // datasheet: no of regs - 1
memset(dev->rx_buff, 0, size);
memset(dev->tx_buff + 2, 0, size);
transfer.speed_hz = SPI_BUS_SPEED;
transfer.tx_buf = dev->tx_buff;
transfer.rx_buf = dev->rx_buff;
transfer.len = size + 2; // 2 is for command tx
transfer.delay_usecs = 40; // datasheet: 4 clocks after each message
transfer.cs_change = 1; // Always toggle CS line after transfer
spi_message_init(&msg);
spi_message_add_tail(&transfer, &msg);
ret = spi_sync(dev->spi, &msg);
//printk(KERN_DEBUG "ecg: %s %x\n", __func__, dev->rx_buff[0]);
if (unlikely(ret))
return ret;
ret = copy_to_user(buf, dev->rx_buff, size);
return ret;
}
static int ads_cdev_open(struct inode *inode, struct file *filp)
{
struct ads1292_dev *ads = container_of(inode->i_cdev, struct ads1292_dev, dev);
char buff[1];
printk(KERN_DEBUG "ecg: %s\n", __func__);
filp->private_data = ads;
gpio_set_value_cansleep(ads1292_gpio_start.gpio, 0);
ads_send_byte(&ads->chip, ADS1292_RESET);
mdelay(1);
ads_send_byte(&ads->chip, ADS1292_SDATAC);
mdelay(10);
ads_send_RREG(&ads->chip, buff, ADS1292_ID, 1);
printk(KERN_DEBUG "ecg: ID - %x\n", buff[0]);
return 0;
}

How to do non-blocking keyboard input on console app using Swift?

I want to make a simple console game using Swift 5. I need to read keyboard input without blocking the game animation (using emojis). The game keeps on going while there's no keyboard input, but will react accordingly if there are ones.
I've seen some example how to do it in other languages such as C and Python. I knew Swift has Darwin module that provide many POSIX functions. However, those C codes seem incompatible with Swift 5.
For example, how to convert the C code below into Swift? There's no FD_ZERO nor FD_SET in Darwin module.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
int main(int argc, char *argv[])
{
int key;
printf("press a key: ");
fflush(stdout);
set_conio_terminal_mode();
while (1) {
if (kbhit()) {
key = getch();
if (key == 13) {
printf("\n\r");
break;
} else if (key >= 20) {
printf("%c, ", key);
fflush(stdout);
}
}
else {
/* do some work */
printf(".");
usleep(10);
printf(".");
usleep(10);
printf(".");
usleep(10);
printf("\e[3D");
usleep(10);
}
}
reset_terminal_mode();
}
I expect swifty code to do the same thing in Swift.
The termios functions translate almost one-to-one to Swift:
#if os(Linux)
import Glibc
#else
import Darwin
#endif
var orig_termios = termios()
func reset_terminal_mode() {
tcsetattr(0, TCSANOW, &orig_termios)
}
func set_conio_terminal_mode() {
tcgetattr(0, &orig_termios)
var new_termios = orig_termios
atexit(reset_terminal_mode)
cfmakeraw(&new_termios)
tcsetattr(0, TCSANOW, &new_termios)
}
set_conio_terminal_mode()
The problem with select() is that FD_ZERO etc are “non-trivial” macros and not imported into Swift. But you can use poll() instead:
func kbhit() -> Bool {
var fds = [ pollfd(fd: STDIN_FILENO, events: Int16(POLLIN), revents: 0) ]
let res = poll(&fds, 1, 0)
return res > 0
}
An alternative is to use the Dispatch framework. Here is a simple example which might help you get started. A dispatch source is used to wait asynchronously for available input, which is then appended to an array, from where it is retrieved in the getch() function. A serial queue is used to synchronize access to the array.
import Dispatch
let stdinQueue = DispatchQueue(label: "my.serial.queue")
var inputCharacters: [CChar] = []
let stdinSource = DispatchSource.makeReadSource(fileDescriptor: STDIN_FILENO, queue: stdinQueue)
stdinSource.setEventHandler(handler: {
var c = CChar()
if read(STDIN_FILENO, &c, 1) == 1 {
inputCharacters.append(c)
}
})
stdinSource.resume()
// Return next input character, or `nil` if there is none.
func getch() -> CChar? {
return stdinQueue.sync {
inputCharacters.isEmpty ? nil : inputCharacters.remove(at: 0)
}
}

Can kernel module take initiative to send message to user space with netlink?

I am trying to run following code, which was copied from here. I have made few changes to run it with older kernel versions.
When I insert kernel module, nlmsg_multicast() fails and logs as nlmsg_multicast() error: -3 in /var/log/messages.
While running user space program, socket() fails.
What exactly I want to do is,
kernel module creates a socket, regardless of any process in user space
kernel module send some events to user space
If any process in user space reply to an event, kernel module process on that reply
Since, It may happen that no process in user space available to reply on event, even in that case module must send event and wait for a while for response.
Is it possible to send first message from kernel module to a process in user space? How can I do this?
Kernel module code:
Makefile
obj-m := foo.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
foo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYGRP 21
static struct sock *nl_sk = NULL;
static void send_to_user(void)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
char *msg = "Hello from kernel";
int msg_size = strlen(msg) + 1;
int res;
pr_info("Creating skb.\n");
skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_KERNEL);
if (!skb) {
pr_err("Allocation failure.\n");
return;
}
nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
strcpy(nlmsg_data(nlh), msg);
pr_info("Sending skb.\n");
res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL);
if (res < 0)
pr_info("nlmsg_multicast() error: %d\n", res);
else
pr_info("Success.\n");
}
static int __init hello_init(void)
{
pr_info("Inserting hello module.\n");
//nl_sk = netlink_kernel_create(&init_net, MYPROTO, NULL);
nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, NULL, NULL, THIS_MODULE);
if (!nl_sk) {
pr_err("Error creating socket.\n");
return -10;
}
send_to_user();
netlink_kernel_release(nl_sk);
return 0;
}
static void __exit hello_exit(void)
{
pr_info("Exiting hello module.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
The user space program:
(Compiled with gcc somename.c)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYMGRP 21
int open_netlink(void)
{
int sock;
struct sockaddr_nl addr;
int group = MYMGRP;
sock = socket(AF_NETLINK, SOCK_RAW, MYPROTO);
if (sock < 0) {
printf("sock < 0.\n");
return sock;
}
memset((void *) &addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
/* This doesn't work for some reason. See the setsockopt() below. */
addr.nl_groups = MYMGRP;
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("bind < 0.\n");
return -1;
}
/*
* 270 is SOL_NETLINK. See
* http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.1#L314
* and
* https://stackoverflow.com/questions/17732044/
*/
/*if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
printf("setsockopt < 0\n");
return -1;
}*/
return sock;
}
void read_event(int sock)
{
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
char buffer[65536];
int ret;
iov.iov_base = (void *) buffer;
iov.iov_len = sizeof(buffer);
msg.msg_name = (void *) &(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("Ok, listening.\n");
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
printf("ret < 0.\n");
else
printf("Received message payload: %s\n", NLMSG_DATA((struct nlmsghdr *) &buffer));
}
int main(int argc, char *argv[])
{
int nls;
nls = open_netlink();
if (nls < 0)
return nls;
while (1)
read_event(nls);
return 0;
}
Thank you for your time!
This looks like bad design (because upper layers should depend on lower layers, not the other way around). But if you're convinced the kernel cannot sit idle or operate using default configuration until userspace can fetch info, then first install this tool (might want to read the core guide too), and then do something like this:
Kernel:
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
#define MYPROTO NETLINK_USERSOCK
#define MYGRP 22
static struct sock *nl_sk;
static struct timer_list timer;
void try_send(unsigned long data)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
char *msg = "Hello from kernel";
int msg_size = strlen(msg) + 1;
int res;
skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_ATOMIC);
if (!skb) {
pr_err("Allocation failure.\n");
return;
}
nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
strcpy(nlmsg_data(nlh), msg);
pr_info("Sending multicast.\n");
res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_ATOMIC);
if (res < 0) {
pr_info("nlmsg_multicast() error: %d. Will try again later.\n", res);
/* Wait 1 second. */
mod_timer(&timer, jiffies + msecs_to_jiffies(1000));
} else {
pr_info("Success.\n");
}
}
static int handle_netlink_message(struct sk_buff *skb_in, struct nlmsghdr *nl_hdr)
{
char *hello;
hello = NLMSG_DATA(nl_hdr);
pr_info("Userspace says '%s.'\n", hello);
return 0;
}
static void receive_answer(struct sk_buff *skb)
{
netlink_rcv_skb(skb, &handle_netlink_message);
}
static int __init hello_init(void)
{
pr_info("Inserting module.\n");
nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, receive_answer, NULL, THIS_MODULE);
if (!nl_sk) {
pr_err("Error creating socket.\n");
return -10;
}
init_timer(&timer);
timer.function = try_send;
timer.expires = jiffies + 1000;
timer.data = 0;
add_timer(&timer);
return 0;
}
static void __exit hello_exit(void)
{
del_timer_sync(&timer);
netlink_kernel_release(nl_sk);
pr_info("Exiting module.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
User (I'm compiling using gcc usr.c -I/usr/include/libnl3 -lnl-3 -Wall, your mileage may vary):
#include <netlink/netlink.h>
#include <netlink/msg.h>
#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 22
struct nl_sock *sk;
void respond_to_kernel(void)
{
char *response = "foo bar";
int error;
error = nl_send_simple(sk, 12345, NLMSG_DONE, response, strlen(response) + 1);
if (error < 0) {
printf("nl_send_simple() threw errcode %d.\n", error);
printf("libnl's message: %s", nl_geterror(error));
} else {
printf("Responded %d bytes.\n", error);
}
}
int receive_kernel_request(struct nl_msg *msg, void *arg)
{
char *hello;
hello = nlmsg_data(nlmsg_hdr(msg));
printf("Kernel says '%s'.\n", hello);
respond_to_kernel();
return 0;
}
int prepare_socket(void)
{
int error;
sk = nl_socket_alloc();
if (!sk) {
printf("nl_socket_alloc() returned NULL.\n");
return -1;
}
nl_socket_disable_seq_check(sk);
error = nl_socket_modify_cb(sk, NL_CB_FINISH, NL_CB_CUSTOM, receive_kernel_request, NULL);
if (error < 0) {
printf("Could not register callback function. Errcode: %d\n", error);
goto fail;
}
error = nl_connect(sk, MYPROTO);
if (error < 0) {
printf("Connection failed: %d\n", error);
goto fail;
}
error = nl_socket_add_memberships(sk, MYMGRP, 0);
if (error) {
printf("Could not register to the multicast group. %d\n", error);
goto fail;
}
return 0;
fail:
printf("libnl's message: %s\n", nl_geterror(error));
nl_socket_free(sk);
return error;
}
int wait_for_kernel_message(void)
{
int error;
printf("Waiting for kernel request...\n");
error = nl_recvmsgs_default(sk);
if (error < 0) {
printf("nl_send_simple() threw errcode %d.\n", error);
printf("libnl's message: %s\n", nl_geterror(error));
return error;
}
return 0;
}
void destroy_socket(void)
{
nl_socket_free(sk);
}
int main(int argc, char *argv[])
{
int error;
error = prepare_socket();
if (error)
return error;
error = wait_for_kernel_message();
destroy_socket();
return error;
}
Tested on kernel 3.2. (Sorry; that's the lowest I have right now.)
This is an example without libnl.
I put all functions in a one file. The coding style is not good. It is only for an example.
I hope it's helpful for you.
I have tested the code in Ubuntu 15.04 which's kernel is kernel 3.19.0-15.
Kernel Module
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/genetlink.h>
static struct timer_list timer;
/* Code based on http://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group/33578010#33578010 */
/**
* This callback runs whenever the socket receives messages.
* We don't use it now, but Linux complains if we don't define it.
*/
static int hello(struct sk_buff *skb, struct genl_info *info)
{
pr_info("Received a message in kernelspace.\n");
return 0;
}
/**
* Attributes are fields of data your messages will contain.
* The designers of Netlink really want you to use these instead of just dumping
* data to the packet payload... and I have really mixed feelings about it.
*/
enum attributes {
/*
* The first one has to be a throwaway empty attribute; I don't know
* why.
* If you remove it, ATTR_HELLO (the first one) stops working, because
* it then becomes the throwaway.
*/
ATTR_DUMMY,
ATTR_HELLO,
ATTR_FOO,
/* This must be last! */
__ATTR_MAX,
};
/**
* Here you can define some constraints for the attributes so Linux will
* validate them for you.
*/
static struct nla_policy policies[] = {
[ATTR_HELLO] = { .type = NLA_STRING, },
[ATTR_FOO] = { .type = NLA_U32, },
};
/**
* Message type codes. All you need is a hello sorta function, so that's what
* I'm defining.
*/
enum commands {
COMMAND_HELLO,
/* This must be last! */
__COMMAND_MAX,
};
/**
* Actual message type definition.
*/
struct genl_ops ops[] = {
{
.cmd = COMMAND_HELLO,
.flags = 0,
.policy = policies,
.doit = hello,
.dumpit = NULL,
},
};
/**
* A Generic Netlink family is a group of listeners who can and want to speak
* your language.
* Anyone who wants to hear your messages needs to register to the same family
* as you.
*/
struct genl_family family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "PotatoFamily",
.version = 1,
.maxattr = __ATTR_MAX,
};
/**
* And more specifically, anyone who wants to hear messages you throw at
* specific multicast groups need to register themselves to the same multicast
* group, too.
*/
struct genl_multicast_group groups[] = {
{ .name = "PotatoGroup" },
};
void send_multicast(unsigned long arg)
{
struct sk_buff *skb;
void *msg_head;
unsigned char *msg = "TEST";
int error;
pr_info("----- Running timer -----\n");
pr_info("Newing message.\n");
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
pr_err("genlmsg_new() failed.\n");
goto end;
}
pr_info("Putting message.\n");
msg_head = genlmsg_put(skb, 0, 0, &family, 0, COMMAND_HELLO);
if (!msg_head) {
pr_err("genlmsg_put() failed.\n");
kfree_skb(skb);
goto end;
}
pr_info("Nla_putting string.\n");
error = nla_put_string(skb, ATTR_HELLO, msg);
if (error) {
pr_err("nla_put_string() failed: %d\n", error);
kfree_skb(skb);
goto end;
}
pr_info("Nla_putting integer.\n");
error = nla_put_u32(skb, ATTR_FOO, 12345);
if (error) {
pr_err("nla_put_u32() failed: %d\n", error);
kfree_skb(skb);
goto end;
}
pr_info("Ending message.\n");
genlmsg_end(skb, msg_head);
pr_info("Multicasting message.\n");
/*
* The family has only one group, so the group ID is just the family's
* group offset.
* mcgrp_offset is supposed to be private, so use this value for debug
* purposes only.
*/
pr_info("The group ID is %u.\n", family.mcgrp_offset);
error = genlmsg_multicast_allns(&family, skb, 0, 0, GFP_KERNEL);
if (error) {
pr_err("genlmsg_multicast_allns() failed: %d\n", error);
pr_err("(This can happen if nobody is listening. "
"Because it's not that unexpected, "
"you might want to just ignore this error.)\n");
goto end;
}
pr_info("Success.\n");
end:
mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}
static int init_socket(void)
{
int error;
pr_info("Registering family.\n");
error = genl_register_family_with_ops_groups(&family, ops, groups);
if (error)
pr_err("Family registration failed: %d\n", error);
return error;
}
static void initialize_timer(void)
{
pr_info("Starting timer.\n");
init_timer(&timer);
timer.function = send_multicast;
timer.expires = 0;
timer.data = 0;
mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}
static int __init hello_init(void)
{
int error;
error = init_socket();
if (error)
return error;
initialize_timer();
pr_info("Hello module registered.\n");
return 0;
}
static void __exit hello_exit(void)
{
del_timer_sync(&timer);
genl_unregister_family(&family);
pr_info("Hello removed.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Kernel Module Makefile
PWD := $(shell pwd)
KVERSION := $(shell uname -r)
KERNEL_DIR = /usr/src/linux-headers-$(KVERSION)/
MODULE_NAME = genl_kern_grp
obj-m := $(MODULE_NAME).o
all:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) M=$(PWD) clean
User Code - main.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <linux/genetlink.h>
/* Code based on libnl-3 */
/* Code based on http://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group/33578010#33578010 */
/* Code based on http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html */
/* Code based on http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html */
/* Based on libnl-3 attr.h */
/**
* #ingroup attr
* Basic attribute data types
*
* See section #core_doc{core_attr_parse,Attribute Parsing} for more details.
*/
enum {
NLA_UNSPEC, /**< Unspecified type, binary data chunk */
NLA_U8, /**< 8 bit integer */
NLA_U16, /**< 16 bit integer */
NLA_U32, /**< 32 bit integer */
NLA_U64, /**< 64 bit integer */
NLA_STRING, /**< NUL terminated character string */
NLA_FLAG, /**< Flag */
NLA_MSECS, /**< Micro seconds (64bit) */
NLA_NESTED, /**< Nested attributes */
NLA_NESTED_COMPAT,
NLA_NUL_STRING,
NLA_BINARY,
NLA_S8,
NLA_S16,
NLA_S32,
NLA_S64,
__NLA_TYPE_MAX,
};
#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
/**
* #ingroup attr
* Attribute validation policy.
*
* See section #core_doc{core_attr_parse,Attribute Parsing} for more details.
*/
struct nla_policy {
/** Type of attribute or NLA_UNSPEC */
uint16_t type;
/** Minimal length of payload required */
uint16_t minlen;
/** Maximal length of payload allowed */
uint16_t maxlen;
};
/**
* Attributes and commands have to be the same as in kernelspace, so you might
* want to move these enums to a .h and just #include that from both files.
*/
enum attributes {
ATTR_DUMMY,
ATTR_HELLO,
ATTR_FOO,
/* This must be last! */
__ATTR_MAX,
};
enum commands {
COMMAND_HELLO,
/* This must be last! */
__COMMAND_MAX,
};
/* Generic macros for dealing with netlink sockets. Might be duplicated
* elsewhere. It is recommended that commercial grade applications use
* libnl or libnetlink and use the interfaces provided by the library
*/
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
/* Family string */
#define GEN_FAMILY_STR "PotatoFamily"
#define GEN_ML_GRP_STR "PotatoGroup"
/* SOL_NETLINK is only defined in <kernel src>/include/linux/socket.h
* It is not defined in <kernel src>/include/uapi/linux/socket.h
* Thus, copy the define to here if we don't include kernel header
*/
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
/**
* #ingroup attr
* Iterate over a stream of attributes
* #arg pos loop counter, set to current attribute
* #arg head head of attribute stream
* #arg len length of attribute stream
* #arg rem initialized to len, holds bytes currently remaining in stream
*/
#define nla_for_each_attr(pos, head, len, rem) \
for (pos = head, rem = len; \
nla_ok(pos, rem); \
pos = nla_next(pos, &(rem)))
/**
* #ingroup attr
* Iterate over a stream of nested attributes
* #arg pos loop counter, set to current attribute
* #arg nla attribute containing the nested attributes
* #arg rem initialized to len, holds bytes currently remaining in stream
*/
#define nla_for_each_nested(pos, nla, rem) \
for (pos = nla_data(nla), rem = nla_len(nla); \
nla_ok(pos, rem); \
pos = nla_next(pos, &(rem)))
/* Variables used for netlink */
int nl_fd; /* netlink socket's file descriptor */
struct sockaddr_nl nl_address; /* netlink socket address */
int nl_family_id; /* The family ID resolved by the netlink controller for this userspace program */
int nl_rxtx_length; /* Number of bytes sent or received via send() or recv() */
struct nlattr *nl_na; /* pointer to netlink attributes structure within the payload */
struct { /* memory for netlink request and response messages - headers are included */
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
} nl_request_msg, nl_response_msg;
/* Base on libnl-3 attr.c */
/**
* Return type of the attribute.
* #arg nla Attribute.
*
* #return Type of attribute.
*/
int nla_type(const struct nlattr *nla)
{
return nla->nla_type & NLA_TYPE_MASK;
}
/**
* Return pointer to the payload section.
* #arg nla Attribute.
*
* #return Pointer to start of payload section.
*/
void *nla_data(const struct nlattr *nla)
{
return (char *) nla + NLA_HDRLEN;
}
/**
* Return length of the payload .
* #arg nla Attribute
*
* #return Length of payload in bytes.
*/
int nla_len(const struct nlattr *nla)
{
return nla->nla_len - NLA_HDRLEN;
}
/**
* Check if the attribute header and payload can be accessed safely.
* #arg nla Attribute of any kind.
* #arg remaining Number of bytes remaining in attribute stream.
*
* Verifies that the header and payload do not exceed the number of
* bytes left in the attribute stream. This function must be called
* before access the attribute header or payload when iterating over
* the attribute stream using nla_next().
*
* #return True if the attribute can be accessed safely, false otherwise.
*/
int nla_ok(const struct nlattr *nla, int remaining)
{
return remaining >= sizeof(*nla) &&
nla->nla_len >= sizeof(*nla) &&
nla->nla_len <= remaining;
}
/**
* Return next attribute in a stream of attributes.
* #arg nla Attribute of any kind.
* #arg remaining Variable to count remaining bytes in stream.
*
* Calculates the offset to the next attribute based on the attribute
* given. The attribute provided is assumed to be accessible, the
* caller is responsible to use nla_ok() beforehand. The offset (length
* of specified attribute including padding) is then subtracted from
* the remaining bytes variable and a pointer to the next attribute is
* returned.
*
* nla_next() can be called as long as remainig is >0.
*
* #return Pointer to next attribute.
*/
struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
{
int totlen = NLA_ALIGN(nla->nla_len);
*remaining -= totlen;
return (struct nlattr *) ((char *) nla + totlen);
}
static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
[NLA_U8] = sizeof(uint8_t),
[NLA_U16] = sizeof(uint16_t),
[NLA_U32] = sizeof(uint32_t),
[NLA_U64] = sizeof(uint64_t),
[NLA_STRING] = 1,
[NLA_FLAG] = 0,
};
static int validate_nla(const struct nlattr *nla, int maxtype,
const struct nla_policy *policy)
{
const struct nla_policy *pt;
unsigned int minlen = 0;
int type = nla_type(nla);
if (type < 0 || type > maxtype)
return 0;
pt = &policy[type];
if (pt->type > NLA_TYPE_MAX)
return -1;
if (pt->minlen)
minlen = pt->minlen;
else if (pt->type != NLA_UNSPEC)
minlen = nla_attr_minlen[pt->type];
if (nla_len(nla) < minlen)
return -2;
if (pt->maxlen && nla_len(nla) > pt->maxlen)
return -3;
if (pt->type == NLA_STRING) {
const char *data = nla_data(nla);
if (data[nla_len(nla) - 1] != '\0')
return -4;
}
return 0;
}
/**
* Create attribute index based on a stream of attributes.
* #arg tb Index array to be filled (maxtype+1 elements).
* #arg maxtype Maximum attribute type expected and accepted.
* #arg head Head of attribute stream.
* #arg len Length of attribute stream.
* #arg policy Attribute validation policy.
*
* Iterates over the stream of attributes and stores a pointer to each
* attribute in the index array using the attribute type as index to
* the array. Attribute with a type greater than the maximum type
* specified will be silently ignored in order to maintain backwards
* compatibility. If \a policy is not NULL, the attribute will be
* validated using the specified policy.
*
* #see nla_validate
* #return 0 on success or a negative error code.
*/
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
struct nla_policy *policy)
{
struct nlattr *nla;
int rem, err;
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
nla_for_each_attr(nla, head, len, rem) {
int type = nla_type(nla);
if (type > maxtype)
continue;
if (policy) {
err = validate_nla(nla, maxtype, policy);
if (err < 0)
goto errout;
}
if (tb[type])
fprintf(stderr, "Attribute of type %#x found multiple times in message, "
"previous attribute is being ignored.\n", type);
tb[type] = nla;
}
if (rem > 0)
fprintf(stderr, "netlink: %d bytes leftover after parsing "
"attributes.\n", rem);
err = 0;
errout:
return err;
}
/**
* Create attribute index based on nested attribute
* #arg tb Index array to be filled (maxtype+1 elements).
* #arg maxtype Maximum attribute type expected and accepted.
* #arg nla Nested Attribute.
* #arg policy Attribute validation policy.
*
* Feeds the stream of attributes nested into the specified attribute
* to nla_parse().
*
* #see nla_parse
* #return 0 on success or a negative error code.
*/
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
struct nla_policy *policy)
{
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
}
static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING,
.maxlen = GENL_NAMSIZ },
[CTRL_ATTR_VERSION] = { .type = NLA_U32 },
[CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 },
[CTRL_ATTR_MAXATTR] = { .type = NLA_U32 },
[CTRL_ATTR_OPS] = { .type = NLA_NESTED },
[CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
};
static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = {
[CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING },
[CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 },
};
int genlctrl_msg_parse(struct nlmsghdr *nlh, int *family_id, char **family_name,
int *mcast_id, char **mcast_name)
{
struct nlattr *tb[CTRL_ATTR_MAX+1];
struct nlattr *nla_hdr;
int nla_length;
int ret = 0;
nla_hdr = (struct nlattr *)((unsigned char *) nlh + NLMSG_HDRLEN + GENL_HDRLEN);
nla_length = nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
if(ret = nla_parse(tb, CTRL_ATTR_MAX, nla_hdr, nla_length, ctrl_policy)) {
fprintf(stderr, "nla_parse error! ret = %d\n", ret);
return -1;
}
if (tb[CTRL_ATTR_FAMILY_ID])
*family_id = *(const uint16_t *) nla_data(tb[CTRL_ATTR_FAMILY_ID]);
if (tb[CTRL_ATTR_FAMILY_NAME])
*family_name = (char *) nla_data(tb[CTRL_ATTR_FAMILY_NAME]);
if (tb[CTRL_ATTR_MCAST_GROUPS]) {
struct nlattr *nla, *grp_attr;
int remaining, err;
grp_attr = tb[CTRL_ATTR_MCAST_GROUPS];
nla_for_each_nested(nla, grp_attr, remaining) {
struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1];
int id = 0;
char *name = NULL;
err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla,
family_grp_policy);
if (err < 0) {
fprintf(stderr, "nla_parse_nested error! err = %d\n", err);
return -1;
}
if (tb[CTRL_ATTR_MCAST_GRP_ID])
id = *(const uint32_t *) nla_data(tb[CTRL_ATTR_MCAST_GRP_ID]);
if (tb[CTRL_ATTR_MCAST_GRP_NAME])
name = (char *) nla_data(tb[CTRL_ATTR_MCAST_GRP_NAME]);
if (id || name) {
*mcast_id = id;
*mcast_name = name;
}
}
}
return 0;
}
void genlmsg_recv(void) {
struct nlmsghdr *nlh;
struct nlattr *tb[__ATTR_MAX];
struct nlattr *nla_hdr;
int nla_length;
int ret = 0;
while(1)
{
memset(&nl_response_msg, 0, sizeof(nl_response_msg));
nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
if (nl_rxtx_length < 0) {
perror("recv()");
goto out;
}
nlh = &nl_response_msg.n;
nla_hdr = (struct nlattr *)((unsigned char *) nlh + NLMSG_HDRLEN + GENL_HDRLEN);
nla_length = nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
if(ret = nla_parse(tb, __ATTR_MAX-1, nla_hdr, nla_length, NULL)) {
fprintf(stderr, "nla_parse error! ret = %d\n", ret);
goto out;
}
if (tb[1])
printf("ATTR_HELLO: len:%u type:%u data:%s\n", tb[1]->nla_len,
tb[1]->nla_type, (char *)nla_data(tb[1]));
else
printf("ATTR_HELLO: null\n");
if (tb[2])
printf("ATTR_FOO: len:%u type:%u data:%u\n", tb[2]->nla_len,
tb[2]->nla_type, *((__u32 *)nla_data(tb[2])));
else
printf("ATTR_FOO: null\n");
}
out:
return;
}
int main(void) {
struct nlattr *nla1, *nla2;
int len, rem, remaining;
struct nlmsghdr *nlh;
int family_id;
char *family_name;
int mcast_id;
char *mcast_name;
int err;
/* Step 1: Open the socket. Note that protocol = NETLINK_GENERIC */
nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (nl_fd < 0) {
perror("socket()");
return -1;
}
/* Step 2: Bind the socket. */
memset(&nl_address, 0, sizeof(nl_address));
nl_address.nl_family = AF_NETLINK;
nl_address.nl_groups = 0;
if (bind(nl_fd, (struct sockaddr *) &nl_address, sizeof(nl_address)) < 0) {
perror("bind()");
goto out;
}
/* Step 3: Resolve the family ID corresponding to the string GEN_FAMILY_STR */
/* Populate the netlink header */
nl_request_msg.n.nlmsg_type = GENL_ID_CTRL;
nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST;
nl_request_msg.n.nlmsg_seq = 0;
nl_request_msg.n.nlmsg_pid = getpid();
nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
/* Populate the payload's "family header" : which in our case is genlmsghdr */
nl_request_msg.g.cmd = CTRL_CMD_GETFAMILY;
nl_request_msg.g.version = 0x1;
/* Populate the payload's "netlink attributes" */
nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg); /* get location of genl data where to put */
nl_na->nla_type = CTRL_ATTR_FAMILY_NAME;
nl_na->nla_len = strlen(GEN_FAMILY_STR) + 1 + NLA_HDRLEN;
strcpy(NLA_DATA(nl_na), GEN_FAMILY_STR); /* Family name length can be upto 16 chars including \0 */
nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len);
memset(&nl_address, 0, sizeof(nl_address));
nl_address.nl_family = AF_NETLINK;
/* Send the family ID request message to the netlink controller */
nl_rxtx_length = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len,
0, (struct sockaddr *) &nl_address, sizeof(nl_address));
if (nl_rxtx_length != nl_request_msg.n.nlmsg_len) {
perror("sendto()");
goto out;
}
/* Wait for the response message */
nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
if (nl_rxtx_length < 0) {
perror("recv()");
goto out;
}
/* Validate response message */
if (!NLMSG_OK((&nl_response_msg.n), nl_rxtx_length)) {
fprintf(stderr, "family ID request : invalid message\n");
goto out;
}
if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { /* error */
fprintf(stderr, "family ID request : receive error\n");
goto out;
}
/* Step 4: Extract family ID and mcast group ID*/
nlh = &nl_response_msg.n;
genlctrl_msg_parse(nlh, &family_id, &family_name, &mcast_id, &mcast_name);
printf("[INFO] family_id = %d, family_name = %s\n", family_id, family_name);
printf("[INFO] mcast_id = %d, mcast_name = %s\n", mcast_id, mcast_name);
/* Step 5: Add to mulitcast group */
err = setsockopt(nl_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &mcast_id, sizeof(mcast_id));
if (err < 0) {
perror ("setsockopt()");
goto out;
}
/* Step 6: Receive multicast data */
genlmsg_recv();
/* Step 7: Close the socket and quit */
close(nl_fd);
return 0;
out:
close(nl_fd);
return -1;
}
User Code Makefile
PWD := $(shell pwd)
TARGET := genl_ml
SRC := main.c
HDR_DIR = /usr/include/
LDFLAGS =
all:
gcc $(SRC) $(LDFLAGS) -o $(TARGET)
clean:
rm -fr $(TARGET)

Error in lex program?

I was making this very simple lex program (just an introductory program). But on compiling lex.yy.c, I am getting this error as:
inToPostfix.l:26: error: ‘struct stackoperand’ has no member named ‘top’
inToPostfix.l:32: error: ‘struct stackoperator’ has no member named ‘top’....
I could not make any reason for this error as I already have defined top in the specified structure.
Can you see any reason for it?
Code is posted at http://pastebin.com/d5f059c1d
Here is a diff against your original. It fixes all the problems when compiling:
--- orig.l 2009-11-09 14:55:47.414002041 -0500
+++ kk.l 2009-11-09 14:54:53.386385539 -0500
## -1,14 +1,15 ##
%{
#include<stdio.h>
%}
+ int precedence(char a,char b);
struct stackoperator{
char stack[10];
- int top =-1;
+ int top;
};
struct stackoperand{
int stack[10][2];
- int top =-1;
+ int top;
};
struct stackoperator operator;
struct stackoperand operand;
## -29,6 +30,7 ##
}
[ \t] ;
[\n] {
+ char ch;
while(operator.top!=-1)
{
ch=pop();
Move line 3 to line 16.
You also need to remove the initializers from the structure declarations - at least for C (but the C++ compiler didn't think much of it either).
struct stackoperator
{
char stack[10];
int top =-1;
};
To:
struct stackoperator
{
char stack[10];
int top;
};
In the actions, you also need to declare 'ch'.
You also need to declare your functions - I made them static. This compiles (assuming you have a C99 compiler - the designated initializers won't work with C89 compilers):
%{
#include<stdio.h>
struct stackoperator
{
char stack[10];
int top;
};
struct stackoperand
{
int stack[10][2];
int top;
};
struct stackoperator operator = { .top = -1 };
struct stackoperand operand = { .top = -1 };
int num=0;
static void push(int num,int flag);
static int pop(void);
static int precedence(char a,char b);
%}
%%
[0-9] {num=num*10+(*yytext-'0');push(num,1);}
[-+*/] {
if(precedence(operator.top,*yytext)) {
char ch=pop();
push(ch,0);
operand.stack[operand.top][1]=1;
}
push(*yytext,0);
}
[ \t] ;
[\n] {
char ch;
while(operator.top!=-1)
{
ch=pop();
push(ch,0);
}
int i=0;
while(i<=operand.top)
{
if(operand.stack[operand.top][1]==1)
printf(" %c ",operand.stack[operand.top][0]);
else
printf(" %d ",operand.stack[operand.top][0]);
}
}
%%
static void push(int num,int flag)
{
if(flag)
{ operand.top++;
operand.stack[operand.top][0]=num;
operand.stack[operand.top][1]=0;
}
else
operator.stack[++operator.top]=num;
}
static int pop(void)
{
return operator.stack[operator.top--];
}
static int precedence(char a,char b)
{
if(operator.top==-1)
return 0;
if((a=='*'||a=='/') && (b=='+'||b=='-'))
return 1;
else
return 0;
}